Adjust frontend auth behavior (#24)
This commit is contained in:
parent
5b8440f295
commit
04fbe6fc4d
@ -31,7 +31,9 @@
|
||||
"devDependencies": {
|
||||
"@esm-bundle/chai": "^4.3.4-fix.0",
|
||||
"@lit/localize-tools": "^0.5.0",
|
||||
"@open-wc/testing": "^3.0.3",
|
||||
"@types/color": "^3.0.2",
|
||||
"@types/sinon": "^10.0.6",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
"@web/dev-server-esbuild": "^0.2.16",
|
||||
|
||||
@ -1,9 +1,27 @@
|
||||
import { expect } from "@esm-bundle/chai";
|
||||
import { spy, stub } from "sinon";
|
||||
import { fixture, expect } from "@open-wc/testing";
|
||||
// import { expect } from "@esm-bundle/chai";
|
||||
|
||||
import { App } from "./index";
|
||||
|
||||
describe("App", () => {
|
||||
it("should exist", () => {
|
||||
expect(App).to.exist;
|
||||
describe("browsertrix-app", () => {
|
||||
it("is defined", async () => {
|
||||
const el = await fixture("<browsertrix-app></browsertrix-app>");
|
||||
expect(el).instanceOf(App);
|
||||
});
|
||||
|
||||
it("gets auth state from local storage", async () => {
|
||||
stub(window.localStorage, "getItem").callsFake((key) => {
|
||||
if (key === "authState")
|
||||
return JSON.stringify({
|
||||
username: "test@example.com",
|
||||
});
|
||||
return null;
|
||||
});
|
||||
const el = (await fixture("<browsertrix-app></browsertrix-app>")) as App;
|
||||
|
||||
expect(el.authState).to.eql({
|
||||
username: "test@example.com",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { TemplateResult } from "lit";
|
||||
import { state } from "lit/decorators.js";
|
||||
import { msg, updateWhenLocaleChanges } from "@lit/localize";
|
||||
|
||||
import "./shoelace";
|
||||
@ -13,10 +14,22 @@ import type { ViewState, NavigateEvent } from "./utils/APIRouter";
|
||||
import type { AuthState } from "./types/auth";
|
||||
import theme from "./theme";
|
||||
|
||||
const ROUTES = {
|
||||
home: "/",
|
||||
login: "/log-in",
|
||||
myAccount: "/my-account",
|
||||
"archive-info": "/archive/:aid",
|
||||
"archive-info-tab": "/archive/:aid/:tab",
|
||||
} as const;
|
||||
|
||||
// ===========================================================================
|
||||
export class App extends LiteElement {
|
||||
authState: AuthState | null;
|
||||
router: APIRouter;
|
||||
|
||||
@state()
|
||||
authState: AuthState | null = null;
|
||||
|
||||
@state()
|
||||
viewState: ViewState & {
|
||||
aid?: string;
|
||||
// TODO common tab type
|
||||
@ -31,31 +44,16 @@ export class App extends LiteElement {
|
||||
// history navigation.
|
||||
updateWhenLocaleChanges(this);
|
||||
|
||||
this.authState = null;
|
||||
|
||||
const authState = window.localStorage.getItem("authState");
|
||||
if (authState) {
|
||||
this.authState = JSON.parse(authState);
|
||||
}
|
||||
|
||||
this.router = new APIRouter({
|
||||
home: "/",
|
||||
login: "/log-in",
|
||||
"my-account": "/my-account",
|
||||
"archive-info": "/archive/:aid",
|
||||
"archive-info-tab": "/archive/:aid/:tab",
|
||||
});
|
||||
this.router = new APIRouter(ROUTES);
|
||||
|
||||
this.viewState = this.router.match(window.location.pathname);
|
||||
}
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
viewState: { type: Object },
|
||||
authState: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
window.addEventListener("popstate", (event) => {
|
||||
// if (event.state.view) {
|
||||
@ -71,10 +69,14 @@ export class App extends LiteElement {
|
||||
if (newView.startsWith("http")) {
|
||||
newView = new URL(newView).pathname;
|
||||
}
|
||||
this.viewState = this.router.match(newView);
|
||||
if (this.viewState._route === "login") {
|
||||
this.clearAuthState();
|
||||
|
||||
if (newView === "/log-in" && this.authState) {
|
||||
// Redirect to logged in home page
|
||||
this.viewState = this.router.match(ROUTES.myAccount);
|
||||
} else {
|
||||
this.viewState = this.router.match(newView);
|
||||
}
|
||||
|
||||
//console.log(this.view._route, window.location.href);
|
||||
window.history.pushState(this.viewState, "", this.viewState._path);
|
||||
}
|
||||
@ -149,7 +151,7 @@ export class App extends LiteElement {
|
||||
<div class="w-full flex flex-col md:flex-row">
|
||||
<nav class="md:w-80 md:p-4 md:border-r">
|
||||
<ul class="flex md:flex-col">
|
||||
${navLink({ href: "/my-account", label: "Archives" })}
|
||||
${navLink({ href: ROUTES.myAccount, label: "Archives" })}
|
||||
${navLink({ href: "/users", label: "Users" })}
|
||||
</ul>
|
||||
</nav>
|
||||
@ -161,17 +163,23 @@ export class App extends LiteElement {
|
||||
case "login":
|
||||
return html`<log-in
|
||||
class="w-full md:bg-gray-100 flex items-center justify-center"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@logged-in="${this.onLoggedIn}"
|
||||
.authState="${this.authState}"
|
||||
></log-in>`;
|
||||
|
||||
case "home":
|
||||
return html`<div class="w-full flex items-center justify-center">
|
||||
<sl-button type="primary" size="large" @click="${this.onNeedLogin}">
|
||||
<sl-button
|
||||
type="primary"
|
||||
size="large"
|
||||
@click="${() => this.navigate("/log-in")}"
|
||||
>
|
||||
${msg("Log In")}
|
||||
</sl-button>
|
||||
</div>`;
|
||||
|
||||
case "my-account":
|
||||
case "myAccount":
|
||||
return appLayout(html`<my-account
|
||||
class="w-full"
|
||||
@navigate="${this.onNavigateTo}"
|
||||
@ -206,12 +214,16 @@ export class App extends LiteElement {
|
||||
headers: { Authorization: event.detail.auth },
|
||||
};
|
||||
window.localStorage.setItem("authState", JSON.stringify(this.authState));
|
||||
this.navigate("/my-account");
|
||||
this.navigate(ROUTES.myAccount);
|
||||
}
|
||||
|
||||
onNeedLogin() {
|
||||
onNeedLogin(event?: CustomEvent<{ api: boolean }>) {
|
||||
this.clearAuthState();
|
||||
this.navigate("/log-in");
|
||||
|
||||
if (event?.detail?.api) {
|
||||
// TODO refresh instead of redirect
|
||||
}
|
||||
this.navigate(ROUTES.login);
|
||||
}
|
||||
|
||||
onNavigateTo(event: NavigateEvent) {
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import LiteElement, { html } from "../utils/LiteElement";
|
||||
import { needLogin } from "../utils/auth";
|
||||
import type { Archive, ArchiveConfig } from "../types/archives";
|
||||
import type { AuthState } from "../types/auth";
|
||||
|
||||
@needLogin
|
||||
export class ArchiveConfigsPage extends LiteElement {
|
||||
archive!: Archive & {
|
||||
authState: AuthState;
|
||||
};
|
||||
archive!: Archive;
|
||||
authState!: AuthState;
|
||||
configs: ArchiveConfig;
|
||||
|
||||
static get properties() {
|
||||
@ -16,14 +17,9 @@ export class ArchiveConfigsPage extends LiteElement {
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
if (!this.archive?.authState) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await this.apiFetch(
|
||||
`/archives/${this.archive.aid}/crawlconfigs`,
|
||||
this.archive.authState
|
||||
this.authState!
|
||||
);
|
||||
this.configs = res.crawl_configs;
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import LiteElement, { html } from "../utils/LiteElement";
|
||||
import { needLogin } from "../utils/auth";
|
||||
import type { Archive } from "../types/archives";
|
||||
import type { AuthState } from "../types/auth";
|
||||
|
||||
@needLogin
|
||||
export class ArchivePage extends LiteElement {
|
||||
authState: AuthState = null;
|
||||
aid?: Archive["aid"];
|
||||
@ -48,8 +50,8 @@ export class ArchivePage extends LiteElement {
|
||||
? html`<btrix-archive-configs
|
||||
.archive=${{
|
||||
aid: this.aid!,
|
||||
authState: this.authState,
|
||||
}}
|
||||
.authState=${this.authState}
|
||||
></btrix-archive-configs>`
|
||||
: ""}
|
||||
</div>
|
||||
|
||||
@ -3,9 +3,6 @@ import LiteElement, { html } from "../utils/LiteElement";
|
||||
import type { Auth } from "../types/auth";
|
||||
|
||||
export class LogInPage extends LiteElement {
|
||||
@property({ type: Object })
|
||||
auth?: Auth;
|
||||
|
||||
@state()
|
||||
isLoggingIn: boolean = false;
|
||||
|
||||
@ -90,10 +87,6 @@ export class LogInPage extends LiteElement {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
if (!this.auth) {
|
||||
this.loginError = "Unknown login response";
|
||||
}
|
||||
|
||||
this.isLoggingIn = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
import LiteElement, { html } from "../utils/LiteElement";
|
||||
import { needLogin } from "../utils/auth";
|
||||
import type { Archive } from "../types/archives";
|
||||
import type { AuthState } from "../types/auth";
|
||||
|
||||
@needLogin
|
||||
export class MyAccountPage extends LiteElement {
|
||||
archiveList: Archive[] = [];
|
||||
authState: AuthState = null;
|
||||
@ -15,15 +17,10 @@ export class MyAccountPage extends LiteElement {
|
||||
}
|
||||
|
||||
async firstUpdated() {
|
||||
if (!this.authState) {
|
||||
this.dispatchEvent(new CustomEvent("need-login"));
|
||||
return;
|
||||
}
|
||||
|
||||
const data = await this.apiFetch("/archives", this.authState);
|
||||
const data = await this.apiFetch("/archives", this.authState!);
|
||||
this.archiveList = data.archives;
|
||||
|
||||
const data2 = await this.apiFetch("/users/me", this.authState);
|
||||
const data2 = await this.apiFetch("/users/me", this.authState!);
|
||||
this.id = data2.id;
|
||||
}
|
||||
|
||||
|
||||
@ -27,10 +27,27 @@ export default class LiteElement extends LitElement {
|
||||
|
||||
async apiFetch(path: string, auth: Auth) {
|
||||
const resp = await fetch("/api" + path, { headers: auth.headers });
|
||||
|
||||
if (resp.status !== 200) {
|
||||
this.navTo("/log-in");
|
||||
throw new Error("logged out");
|
||||
if (resp.status === 401) {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("need-login", {
|
||||
detail: { api: true },
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// TODO get error details
|
||||
let errorMessage: string;
|
||||
|
||||
try {
|
||||
errorMessage = (await resp.json()).detail;
|
||||
} catch {
|
||||
errorMessage = "Unknown API error";
|
||||
}
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return await resp.json();
|
||||
}
|
||||
}
|
||||
|
||||
26
frontend/src/utils/auth.test.ts
Normal file
26
frontend/src/utils/auth.test.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { spy } from "sinon";
|
||||
import { expect } from "@esm-bundle/chai";
|
||||
|
||||
import * as auth from "./auth";
|
||||
|
||||
describe("auth", () => {
|
||||
describe("needLogin", () => {
|
||||
it("dispatches the correct event on need log in", () => {
|
||||
const dispatchEventSpy = spy();
|
||||
class LiteElementMock {
|
||||
dispatchEvent = dispatchEventSpy;
|
||||
}
|
||||
|
||||
const Element = auth.needLogin(
|
||||
class extends LiteElementMock {
|
||||
authState = null;
|
||||
} as any
|
||||
);
|
||||
|
||||
const element = new Element();
|
||||
element.connectedCallback();
|
||||
|
||||
expect(dispatchEventSpy.getCall(0).firstArg.type).to.equal("need-login");
|
||||
});
|
||||
});
|
||||
});
|
||||
33
frontend/src/utils/auth.ts
Normal file
33
frontend/src/utils/auth.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import LiteElement from "../utils/LiteElement";
|
||||
import type { AuthState } from "../types/auth";
|
||||
|
||||
/**
|
||||
* Block rendering and dispatch event if user is not logged in
|
||||
*
|
||||
* Usage example:
|
||||
* ```ts
|
||||
* @needLogin
|
||||
* MyComponent extends LiteElement {}
|
||||
* ```
|
||||
*/
|
||||
export function needLogin<T extends { new (...args: any[]): LiteElement }>(
|
||||
constructor: T
|
||||
) {
|
||||
return class extends constructor {
|
||||
authState?: AuthState;
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
authState: { type: Object },
|
||||
};
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (this.authState) {
|
||||
super.connectedCallback();
|
||||
} else {
|
||||
this.dispatchEvent(new CustomEvent("need-login"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -59,6 +59,13 @@
|
||||
minimatch "^3.0.4"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
"@esm-bundle/chai@^4.3.4":
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@esm-bundle/chai/-/chai-4.3.4.tgz#74ed4a0794b3a9f9517ff235744ac6f4be0d34dc"
|
||||
integrity sha512-6Tx35wWiNw7X0nLY9RMx8v3EL8SacCFW+eEZOE9Hc+XxmU5HFE2AFEg+GehUZpiyDGwVvPH75ckGlqC7coIPnA==
|
||||
dependencies:
|
||||
"@types/chai" "^4.2.12"
|
||||
|
||||
"@esm-bundle/chai@^4.3.4-fix.0":
|
||||
version "4.3.4-fix.0"
|
||||
resolved "https://registry.yarnpkg.com/@esm-bundle/chai/-/chai-4.3.4-fix.0.tgz#3084cff7eb46d741749f47f3a48dbbdcbaf30a92"
|
||||
@ -176,6 +183,63 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@open-wc/chai-dom-equals@^0.12.36":
|
||||
version "0.12.36"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/chai-dom-equals/-/chai-dom-equals-0.12.36.tgz#ed0eb56b9e98c4d7f7280facce6215654aae9f4c"
|
||||
integrity sha512-Gt1fa37h4rtWPQGETSU4n1L678NmMi9KwHM1sH+JCGcz45rs8DBPx7MUVeGZ+HxRlbEI5t9LU2RGGv6xT2OlyA==
|
||||
dependencies:
|
||||
"@open-wc/semantic-dom-diff" "^0.13.16"
|
||||
"@types/chai" "^4.1.7"
|
||||
|
||||
"@open-wc/dedupe-mixin@^1.3.0":
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/dedupe-mixin/-/dedupe-mixin-1.3.0.tgz#0df5d438285fc3482838786ee81895318f0ff778"
|
||||
integrity sha512-UfdK1MPnR6T7f3svzzYBfu3qBkkZ/KsPhcpc3JYhsUY4hbpwNF9wEQtD4Z+/mRqMTJrKg++YSxIxE0FBhY3RIw==
|
||||
|
||||
"@open-wc/scoped-elements@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/scoped-elements/-/scoped-elements-2.0.1.tgz#6b1c3535f809bd90710574db80093a81e3a1fc2d"
|
||||
integrity sha512-JS6ozxUFwFX3+Er91v9yQzNIaFn7OnE0iESKTbFvkkKdNwvAPtp1fpckBKIvWk8Ae9ZcoI9DYZuT2DDbMPcadA==
|
||||
dependencies:
|
||||
"@lit/reactive-element" "^1.0.0"
|
||||
"@open-wc/dedupe-mixin" "^1.3.0"
|
||||
"@webcomponents/scoped-custom-element-registry" "^0.0.3"
|
||||
|
||||
"@open-wc/semantic-dom-diff@^0.13.16":
|
||||
version "0.13.21"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/semantic-dom-diff/-/semantic-dom-diff-0.13.21.tgz#718b9ec5f9a98935fc775e577ad094ae8d8b7dea"
|
||||
integrity sha512-BONpjHcGX2zFa9mfnwBCLEmlDsOHzT+j6Qt1yfK3MzFXFtAykfzFjAgaxPetu0YbBlCfXuMlfxI4vlRGCGMvFg==
|
||||
|
||||
"@open-wc/semantic-dom-diff@^0.19.5":
|
||||
version "0.19.5"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/semantic-dom-diff/-/semantic-dom-diff-0.19.5.tgz#8d3d7f69140b9ba477a4adf8099c79e0efe18955"
|
||||
integrity sha512-Wi0Fuj3dzqlWClU0y+J4k/nqTcH0uwgOWxZXPyeyG3DdvuyyjgiT4L4I/s6iVShWQvvEsyXnj7yVvixAo3CZvg==
|
||||
dependencies:
|
||||
"@types/chai" "^4.2.11"
|
||||
"@web/test-runner-commands" "^0.5.7"
|
||||
|
||||
"@open-wc/testing-helpers@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/testing-helpers/-/testing-helpers-2.0.2.tgz#ca1833bf76036d9bdc03547415e79b6d502c78f6"
|
||||
integrity sha512-wJlvDmWo+fIbgykRP21YSP9I9Pf/fo2+dZGaWG77Hw0sIuyB+7sNUDJDkL6kMkyyRecPV6dVRmbLt6HuOwvZ1w==
|
||||
dependencies:
|
||||
"@open-wc/scoped-elements" "^2.0.1"
|
||||
lit "^2.0.0"
|
||||
|
||||
"@open-wc/testing@^3.0.3":
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@open-wc/testing/-/testing-3.0.3.tgz#9d122933ef69e06cc9cb3b0d4e425b90117bb9e5"
|
||||
integrity sha512-xJYckO8X9yfWc+ltPlDZjHGTh4ldNmnYsnxNriuUUEEhV5ASdsc+5WEsIS2+9m4lQELj89rNQ7YvhYhawDorhg==
|
||||
dependencies:
|
||||
"@esm-bundle/chai" "^4.3.4"
|
||||
"@open-wc/chai-dom-equals" "^0.12.36"
|
||||
"@open-wc/semantic-dom-diff" "^0.19.5"
|
||||
"@open-wc/testing-helpers" "^2.0.2"
|
||||
"@types/chai" "^4.2.11"
|
||||
"@types/chai-dom" "^0.0.9"
|
||||
"@types/sinon-chai" "^3.2.3"
|
||||
chai-a11y-axe "^1.3.2"
|
||||
|
||||
"@popperjs/core@^2.7.0":
|
||||
version "2.10.2"
|
||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590"
|
||||
@ -226,7 +290,7 @@
|
||||
dependencies:
|
||||
type-detect "4.0.8"
|
||||
|
||||
"@sinonjs/fake-timers@^7.0.4":
|
||||
"@sinonjs/fake-timers@^7.0.4", "@sinonjs/fake-timers@^7.1.0":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz#2524eae70c4910edccf99b2f4e6efc5894aff7b5"
|
||||
integrity sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==
|
||||
@ -274,7 +338,14 @@
|
||||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/chai@^4.2.12":
|
||||
"@types/chai-dom@^0.0.9":
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/chai-dom/-/chai-dom-0.0.9.tgz#77379354efec2568284ca355fff6a4f85f5a66f4"
|
||||
integrity sha512-jj4F2NJog2/GBYsyJ8+NvhnWUBbPY4MUAKLdPJE6+568rw12GGXvj0ycUuP5nndVrnJgozmJAoMTvxvjJATXWw==
|
||||
dependencies:
|
||||
"@types/chai" "*"
|
||||
|
||||
"@types/chai@*", "@types/chai@^4.1.7", "@types/chai@^4.2.11", "@types/chai@^4.2.12":
|
||||
version "4.2.22"
|
||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.22.tgz#47020d7e4cf19194d43b5202f35f75bd2ad35ce7"
|
||||
integrity sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==
|
||||
@ -518,6 +589,21 @@
|
||||
"@types/mime" "^1"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/sinon-chai@^3.2.3":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/sinon-chai/-/sinon-chai-3.2.5.tgz#df21ae57b10757da0b26f512145c065f2ad45c48"
|
||||
integrity sha512-bKQqIpew7mmIGNRlxW6Zli/QVyc3zikpGzCa797B/tRnD9OtHvZ/ts8sYXV+Ilj9u3QRaUEM8xrjgd1gwm1BpQ==
|
||||
dependencies:
|
||||
"@types/chai" "*"
|
||||
"@types/sinon" "*"
|
||||
|
||||
"@types/sinon@*", "@types/sinon@^10.0.6":
|
||||
version "10.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.6.tgz#bc3faff5154e6ecb69b797d311b7cf0c1b523a1d"
|
||||
integrity sha512-6EF+wzMWvBNeGrfP3Nx60hhx+FfwSg1JJBLAAP/IdIUq0EYkqCYf70VT3PhuhPX9eLD+Dp+lNdpb/ZeHG8Yezg==
|
||||
dependencies:
|
||||
"@sinonjs/fake-timers" "^7.1.0"
|
||||
|
||||
"@types/trusted-types@^2.0.2":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
|
||||
@ -718,7 +804,7 @@
|
||||
chrome-launcher "^0.15.0"
|
||||
puppeteer-core "^11.0.0"
|
||||
|
||||
"@web/test-runner-commands@^0.5.10":
|
||||
"@web/test-runner-commands@^0.5.10", "@web/test-runner-commands@^0.5.7":
|
||||
version "0.5.13"
|
||||
resolved "https://registry.yarnpkg.com/@web/test-runner-commands/-/test-runner-commands-0.5.13.tgz#57ea472c00ee2ada99eb9bb5a0371200922707c2"
|
||||
integrity sha512-FXnpUU89ALbRlh9mgBd7CbSn5uzNtr8gvnQZPOvGLDAJ7twGvZdUJEAisPygYx2BLPSFl3/Mre8pH8zshJb8UQ==
|
||||
@ -928,6 +1014,11 @@
|
||||
"@webassemblyjs/ast" "1.11.1"
|
||||
"@xtuc/long" "4.2.2"
|
||||
|
||||
"@webcomponents/scoped-custom-element-registry@^0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@webcomponents/scoped-custom-element-registry/-/scoped-custom-element-registry-0.0.3.tgz#774591a886b0b0e4914717273ba53fd8d5657522"
|
||||
integrity sha512-lpSzgDCGbM99dytb3+J3Suo4+Bk1E13MPnWB42JK8GwxSAxFz+tC7TTv2hhDSIE2IirGNKNKCf3m08ecu6eAsQ==
|
||||
|
||||
"@webpack-cli/configtest@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.4.tgz#f03ce6311c0883a83d04569e2c03c6238316d2aa"
|
||||
@ -1145,6 +1236,11 @@ autoprefixer@^10.3.6:
|
||||
normalize-range "^0.1.2"
|
||||
postcss-value-parser "^4.1.0"
|
||||
|
||||
axe-core@^4.3.3:
|
||||
version "4.3.5"
|
||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.3.5.tgz#78d6911ba317a8262bfee292aeafcc1e04b49cc5"
|
||||
integrity sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==
|
||||
|
||||
axios@^0.22.0:
|
||||
version "0.22.0"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.22.0.tgz#bf702c41fb50fbca4539589d839a077117b79b25"
|
||||
@ -1314,6 +1410,13 @@ caniuse-lite@^1.0.30001260, caniuse-lite@^1.0.30001261:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz#7ce7a6fb482a137585cbc908aaf38e90c53a16a4"
|
||||
integrity sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==
|
||||
|
||||
chai-a11y-axe@^1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/chai-a11y-axe/-/chai-a11y-axe-1.3.2.tgz#77dc5f503901fed4f6097b5b0213ddb00cc891ea"
|
||||
integrity sha512-/jYczmhGUoCfEcsrkJwjecy3PJ31T9FxFdu2BDlAwR/sX1nN3L2XmuPP3tw8iYk6LPqdF7K11wwFr3yUZMv5MA==
|
||||
dependencies:
|
||||
axe-core "^4.3.3"
|
||||
|
||||
chalk@^2.0.0, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user