fix: Crawler proxy selection fixes (#2280)

- Hides proxy form control if there are no proxy servers available
- Fixes org default proxy value not being saved
This commit is contained in:
sua yoo 2025-01-08 16:02:09 -08:00 committed by GitHub
parent d6189eee9a
commit 1260aec976
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 164 additions and 106 deletions

View File

@ -8,6 +8,7 @@
"@formatjs/intl-durationformat": "^0.6.4", "@formatjs/intl-durationformat": "^0.6.4",
"@formatjs/intl-localematcher": "^0.5.9", "@formatjs/intl-localematcher": "^0.5.9",
"@ianvs/prettier-plugin-sort-imports": "^4.2.1", "@ianvs/prettier-plugin-sort-imports": "^4.2.1",
"@lit/context": "^1.1.3",
"@lit/localize": "^0.12.1", "@lit/localize": "^0.12.1",
"@lit/task": "^1.0.0", "@lit/task": "^1.0.0",
"@novnc/novnc": "^1.4.0-beta", "@novnc/novnc": "^1.4.0-beta",

View File

@ -1,10 +1,11 @@
import { localized, msg } from "@lit/localize"; import { localized, msg } from "@lit/localize";
import { type SlSelect } from "@shoelace-style/shoelace"; import type { SlSelect } from "@shoelace-style/shoelace";
import { html } from "lit"; import { html } from "lit";
import { customElement, property, state } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import type { ProxiesAPIResponse, Proxy } from "@/pages/org/types"; import { BtrixElement } from "@/classes/BtrixElement";
import LiteElement from "@/utils/LiteElement"; import type { Proxy } from "@/pages/org/types";
type SelectCrawlerProxyChangeDetail = { type SelectCrawlerProxyChangeDetail = {
value: string | null; value: string | null;
@ -26,30 +27,40 @@ export type SelectCrawlerProxyUpdateEvent =
* Usage example: * Usage example:
* ```ts * ```ts
* <btrix-select-crawler-proxy * <btrix-select-crawler-proxy
* orgId=${orgId} * .proxyServers=${proxyServers}
* on-change=${({value}) => selectedcrawlerProxy = value} * btrix-change=${({value}) => selectedcrawlerProxy = value}
* ></btrix-select-crawler-proxy> * ></btrix-select-crawler-proxy>
* ``` * ```
* *
* @event on-change * @fires btrix-change
*/ */
@customElement("btrix-select-crawler-proxy") @customElement("btrix-select-crawler-proxy")
@localized() @localized()
export class SelectCrawlerProxy extends LiteElement { export class SelectCrawlerProxy extends BtrixElement {
@property({ type: String })
defaultProxyId: string | null = null;
@property({ type: Array })
proxyServers: Proxy[] = [];
@property({ type: String }) @property({ type: String })
proxyId: string | null = null; proxyId: string | null = null;
@property({ type: String })
size?: SlSelect["size"];
@state() @state()
private selectedProxy?: Proxy; private selectedProxy?: Proxy;
@state() @state()
private defaultProxy?: Proxy; private defaultProxy?: Proxy;
@state() public get value() {
private allProxies?: Proxy[]; return this.selectedProxy?.id || "";
}
protected firstUpdated() { protected firstUpdated() {
void this.fetchOrgProxies(); void this.initProxies();
} }
// credit: https://dev.to/jorik/country-code-to-flag-emoji-a21 // credit: https://dev.to/jorik/country-code-to-flag-emoji-a21
private countryCodeToFlagEmoji(countryCode: String): String { private countryCodeToFlagEmoji(countryCode: String): String {
@ -61,10 +72,6 @@ export class SelectCrawlerProxy extends LiteElement {
} }
render() { render() {
/*if (this.crawlerProxys && this.crawlerProxys.length < 2) {
return html``;
}*/
return html` return html`
<sl-select <sl-select
name="proxyId" name="proxyId"
@ -75,15 +82,12 @@ export class SelectCrawlerProxy extends LiteElement {
: msg("No Proxy")} : msg("No Proxy")}
hoist hoist
clearable clearable
size=${ifDefined(this.size)}
@sl-change=${this.onChange} @sl-change=${this.onChange}
@sl-focus=${() => {
// Refetch to keep list up to date
void this.fetchOrgProxies();
}}
@sl-hide=${this.stopProp} @sl-hide=${this.stopProp}
@sl-after-hide=${this.stopProp} @sl-after-hide=${this.stopProp}
> >
${this.allProxies?.map( ${this.proxyServers.map(
(server) => (server) =>
html` <sl-option value=${server.id}> html` <sl-option value=${server.id}>
${server.country_code ${server.country_code
@ -121,7 +125,7 @@ export class SelectCrawlerProxy extends LiteElement {
private onChange(e: Event) { private onChange(e: Event) {
this.stopProp(e); this.stopProp(e);
this.selectedProxy = this.allProxies?.find( this.selectedProxy = this.proxyServers.find(
({ id }) => id === (e.target as SlSelect).value, ({ id }) => id === (e.target as SlSelect).value,
); );
@ -130,7 +134,7 @@ export class SelectCrawlerProxy extends LiteElement {
} }
this.dispatchEvent( this.dispatchEvent(
new CustomEvent<SelectCrawlerProxyChangeDetail>("on-change", { new CustomEvent<SelectCrawlerProxyChangeDetail>("btrix-change", {
detail: { detail: {
value: this.selectedProxy ? this.selectedProxy.id : null, value: this.selectedProxy ? this.selectedProxy.id : null,
}, },
@ -138,63 +142,24 @@ export class SelectCrawlerProxy extends LiteElement {
); );
} }
/** private async initProxies(): Promise<void> {
* Fetch crawler proxies and update internal state const defaultProxyId = this.defaultProxyId;
*/
private async fetchOrgProxies(): Promise<void> {
try {
const data = await this.getOrgProxies();
const defaultProxyId = data.default_proxy_id;
this.allProxies = data.servers; if (!this.defaultProxy) {
this.defaultProxy = this.proxyServers.find(
if (!this.defaultProxy) { ({ id }) => id === defaultProxyId,
this.defaultProxy = this.allProxies.find(
({ id }) => id === defaultProxyId,
);
}
if (this.proxyId && !this.selectedProxy?.id) {
this.selectedProxy = this.allProxies.find(
({ id }) => id === this.proxyId,
);
}
if (!this.selectedProxy) {
this.proxyId = null;
this.dispatchEvent(
new CustomEvent("on-change", {
detail: {
value: null,
},
}),
);
this.selectedProxy = this.allProxies.find(
({ id }) => id === this.proxyId,
);
}
this.dispatchEvent(
new CustomEvent<SelectCrawlerProxyUpdateDetail>("on-update", {
detail: {
show: this.allProxies.length > 1,
},
}),
); );
} catch (e) {
this.notify({
message: msg("Sorry, couldn't retrieve proxies at this time."),
variant: "danger",
icon: "exclamation-octagon",
id: "proxy-retrieve-status",
});
} }
}
private async getOrgProxies(): Promise<ProxiesAPIResponse> { if (this.proxyId && !this.selectedProxy) {
return this.apiFetch<ProxiesAPIResponse>( this.selectedProxy = this.proxyServers.find(
`/orgs/${this.orgId}/crawlconfigs/crawler-proxies`, ({ id }) => id === this.proxyId,
); );
}
if (!this.selectedProxy) {
this.proxyId = null;
}
} }
/** /**

View File

@ -0,0 +1,7 @@
import { createContext } from "@lit/context";
import type { ProxiesAPIResponse } from "@/types/crawler";
export type ProxiesContext = ProxiesAPIResponse | null;
export const proxiesContext = createContext<ProxiesContext>("proxies");

View File

@ -1,5 +1,7 @@
import { consume } from "@lit/context";
import { localized, msg, str } from "@lit/localize"; import { localized, msg, str } from "@lit/localize";
import { type SlInput } from "@shoelace-style/shoelace"; import { type SlInput } from "@shoelace-style/shoelace";
import { nothing } from "lit";
import { import {
customElement, customElement,
property, property,
@ -7,16 +9,21 @@ import {
queryAsync, queryAsync,
state, state,
} from "lit/decorators.js"; } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import queryString from "query-string"; import queryString from "query-string";
import type { Dialog } from "@/components/ui/dialog"; import type { Dialog } from "@/components/ui/dialog";
import { type SelectCrawlerChangeEvent } from "@/components/ui/select-crawler"; import { type SelectCrawlerChangeEvent } from "@/components/ui/select-crawler";
import { type SelectCrawlerProxyChangeEvent } from "@/components/ui/select-crawler-proxy"; import { type SelectCrawlerProxyChangeEvent } from "@/components/ui/select-crawler-proxy";
import { proxiesContext, type ProxiesContext } from "@/context/org";
import LiteElement, { html } from "@/utils/LiteElement"; import LiteElement, { html } from "@/utils/LiteElement";
@localized() @localized()
@customElement("btrix-new-browser-profile-dialog") @customElement("btrix-new-browser-profile-dialog")
export class NewBrowserProfileDialog extends LiteElement { export class NewBrowserProfileDialog extends LiteElement {
@consume({ context: proxiesContext, subscribe: true })
private readonly proxies?: ProxiesContext;
@property({ type: Boolean }) @property({ type: Boolean })
open = false; open = false;
@ -83,14 +90,22 @@ export class NewBrowserProfileDialog extends LiteElement {
(this.crawlerChannel = e.detail.value!)} (this.crawlerChannel = e.detail.value!)}
></btrix-select-crawler> ></btrix-select-crawler>
</div> </div>
<div class="mt-4"> ${this.proxies?.servers.length
<btrix-select-crawler-proxy ? html`
orgId=${this.orgId} <div class="mt-4">
.proxyId="${this.proxyId || ""}" <btrix-select-crawler-proxy
@on-change=${(e: SelectCrawlerProxyChangeEvent) => defaultProxyId=${ifDefined(
(this.proxyId = e.detail.value!)} this.proxies.default_proxy_id ?? undefined,
></btrix-select-crawler-proxy> )}
</div> .proxyServers=${this.proxies.servers}
.proxyId="${this.proxyId || ""}"
@btrix-change=${(e: SelectCrawlerProxyChangeEvent) =>
(this.proxyId = e.detail.value)}
></btrix-select-crawler-proxy>
</div>
`
: nothing}
<input class="invisible size-0" type="submit" /> <input class="invisible size-0" type="submit" />
</form> </form>
<div slot="footer" class="flex justify-between"> <div slot="footer" class="flex justify-between">

View File

@ -1,3 +1,4 @@
import { consume } from "@lit/context";
import { localized, msg, str } from "@lit/localize"; import { localized, msg, str } from "@lit/localize";
import type { import type {
SlChangeEvent, SlChangeEvent,
@ -43,6 +44,7 @@ import type { SelectCrawlerProxyChangeEvent } from "@/components/ui/select-crawl
import type { Tab } from "@/components/ui/tab-list"; import type { Tab } from "@/components/ui/tab-list";
import type { TagInputEvent, TagsChangeEvent } from "@/components/ui/tag-input"; import type { TagInputEvent, TagsChangeEvent } from "@/components/ui/tag-input";
import type { TimeInputChangeEvent } from "@/components/ui/time-input"; import type { TimeInputChangeEvent } from "@/components/ui/time-input";
import { proxiesContext, type ProxiesContext } from "@/context/org";
import { type SelectBrowserProfileChangeEvent } from "@/features/browser-profiles/select-browser-profile"; import { type SelectBrowserProfileChangeEvent } from "@/features/browser-profiles/select-browser-profile";
import type { CollectionsChangeEvent } from "@/features/collections/collections-add"; import type { CollectionsChangeEvent } from "@/features/collections/collections-add";
import type { QueueExclusionTable } from "@/features/crawl-workflows/queue-exclusion-table"; import type { QueueExclusionTable } from "@/features/crawl-workflows/queue-exclusion-table";
@ -188,6 +190,9 @@ type CrawlConfigResponse = {
@localized() @localized()
@customElement("btrix-workflow-editor") @customElement("btrix-workflow-editor")
export class WorkflowEditor extends BtrixElement { export class WorkflowEditor extends BtrixElement {
@consume({ context: proxiesContext, subscribe: true })
private readonly proxies?: ProxiesContext;
@property({ type: String }) @property({ type: String })
configId?: string; configId?: string;
@ -1329,17 +1334,24 @@ https://archiveweb.page/images/${"logo.svg"}`}
></btrix-select-browser-profile> ></btrix-select-browser-profile>
`)} `)}
${this.renderHelpTextCol(infoTextStrings["browserProfile"])} ${this.renderHelpTextCol(infoTextStrings["browserProfile"])}
${inputCol(html` ${this.proxies?.servers.length
<btrix-select-crawler-proxy ? [
orgId=${this.orgId} inputCol(html`
.proxyId="${this.formState.proxyId || ""}" <btrix-select-crawler-proxy
@on-change=${(e: SelectCrawlerProxyChangeEvent) => defaultProxyId=${ifDefined(
this.updateFormState({ this.proxies.default_proxy_id ?? undefined,
proxyId: e.detail.value, )}
})} .proxyServers=${this.proxies.servers}
></btrix-select-crawler-proxy> .proxyId="${this.formState.proxyId || ""}"
`)} @btrix-change=${(e: SelectCrawlerProxyChangeEvent) =>
${this.renderHelpTextCol(infoTextStrings["proxyId"])} this.updateFormState({
proxyId: e.detail.value,
})}
></btrix-select-crawler-proxy>
`),
this.renderHelpTextCol(infoTextStrings["proxyId"]),
]
: nothing}
${inputCol(html` ${inputCol(html`
<sl-radio-group <sl-radio-group
name="scale" name="scale"

View File

@ -1,3 +1,4 @@
import { provide } from "@lit/context";
import { localized, msg, str } from "@lit/localize"; import { localized, msg, str } from "@lit/localize";
import { html, nothing } from "lit"; import { html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators.js"; import { customElement, property, state } from "lit/decorators.js";
@ -15,10 +16,12 @@ import type {
} from "./settings/settings"; } from "./settings/settings";
import { BtrixElement } from "@/classes/BtrixElement"; import { BtrixElement } from "@/classes/BtrixElement";
import { proxiesContext, type ProxiesContext } from "@/context/org";
import type { QuotaUpdateDetail } from "@/controllers/api"; import type { QuotaUpdateDetail } from "@/controllers/api";
import needLogin from "@/decorators/needLogin"; import needLogin from "@/decorators/needLogin";
import type { CollectionSavedEvent } from "@/features/collections/collection-metadata-dialog"; import type { CollectionSavedEvent } from "@/features/collections/collection-metadata-dialog";
import type { SelectJobTypeEvent } from "@/features/crawl-workflows/new-workflow-dialog"; import type { SelectJobTypeEvent } from "@/features/crawl-workflows/new-workflow-dialog";
import type { ProxiesAPIResponse } from "@/types/crawler";
import type { UserOrg } from "@/types/user"; import type { UserOrg } from "@/types/user";
import { isApiError } from "@/utils/api"; import { isApiError } from "@/utils/api";
import type { ViewState } from "@/utils/APIRouter"; import type { ViewState } from "@/utils/APIRouter";
@ -93,6 +96,9 @@ const UUID_REGEX =
@customElement("btrix-org") @customElement("btrix-org")
@needLogin @needLogin
export class Org extends BtrixElement { export class Org extends BtrixElement {
@provide({ context: proxiesContext })
proxies: ProxiesContext = null;
@property({ type: Object }) @property({ type: Object })
viewStateData?: ViewState["data"]; viewStateData?: ViewState["data"];
@ -147,6 +153,7 @@ export class Org extends BtrixElement {
) { ) {
if (this.userOrg) { if (this.userOrg) {
void this.updateOrg(); void this.updateOrg();
void this.updateOrgProxies();
} else { } else {
// Couldn't find org with slug, redirect to first org // Couldn't find org with slug, redirect to first org
const org = this.userInfo.orgs[0] as UserOrg | undefined; const org = this.userInfo.orgs[0] as UserOrg | undefined;
@ -211,6 +218,14 @@ export class Org extends BtrixElement {
} }
} }
private async updateOrgProxies() {
try {
this.proxies = await this.getOrgProxies(this.orgId);
} catch (e) {
console.debug(e);
}
}
async firstUpdated() { async firstUpdated() {
// if slug is actually an orgId (UUID), attempt to lookup the slug // if slug is actually an orgId (UUID), attempt to lookup the slug
// and redirect to the slug url // and redirect to the slug url
@ -229,6 +244,8 @@ export class Org extends BtrixElement {
// Sync URL to create dialog // Sync URL to create dialog
const dialogName = this.getDialogName(); const dialogName = this.getDialogName();
if (dialogName) this.openDialog(dialogName); if (dialogName) this.openDialog(dialogName);
void this.updateOrgProxies();
} }
private getDialogName() { private getDialogName() {
@ -634,6 +651,12 @@ export class Org extends BtrixElement {
return data; return data;
} }
private async getOrgProxies(orgId: string): Promise<ProxiesAPIResponse> {
return this.api.fetch<ProxiesAPIResponse>(
`/orgs/${orgId}/crawlconfigs/crawler-proxies`,
);
}
private async onOrgRemoveMember(e: OrgRemoveMemberEvent) { private async onOrgRemoveMember(e: OrgRemoveMemberEvent) {
void this.removeMember(e.detail.member); void this.removeMember(e.detail.member);
} }
@ -682,9 +705,15 @@ export class Org extends BtrixElement {
icon: "check2-circle", icon: "check2-circle",
id: "user-updated-status", id: "user-updated-status",
}); });
const org = await this.getOrg(this.orgId); const org = await this.getOrg(this.orgId);
AppStateService.updateOrg(org); if (org) {
AppStateService.partialUpdateOrg({
id: org.id,
users: org.users,
});
}
} catch (e) { } catch (e) {
console.debug(e); console.debug(e);
@ -742,7 +771,12 @@ export class Org extends BtrixElement {
} else { } else {
const org = await this.getOrg(this.orgId); const org = await this.getOrg(this.orgId);
AppStateService.updateOrg(org); if (org) {
AppStateService.partialUpdateOrg({
id: org.id,
users: org.users,
});
}
} }
} catch (e) { } catch (e) {
console.debug(e); console.debug(e);

View File

@ -1,3 +1,4 @@
import { consume } from "@lit/context";
import { localized, msg } from "@lit/localize"; import { localized, msg } from "@lit/localize";
import type { SlButton } from "@shoelace-style/shoelace"; import type { SlButton } from "@shoelace-style/shoelace";
import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js"; import { serialize } from "@shoelace-style/shoelace/dist/utilities/form.js";
@ -9,6 +10,8 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { BtrixElement } from "@/classes/BtrixElement"; import { BtrixElement } from "@/classes/BtrixElement";
import type { LanguageSelect } from "@/components/ui/language-select"; import type { LanguageSelect } from "@/components/ui/language-select";
import type { SelectCrawlerProxy } from "@/components/ui/select-crawler-proxy";
import { proxiesContext, type ProxiesContext } from "@/context/org";
import type { QueueExclusionTable } from "@/features/crawl-workflows/queue-exclusion-table"; import type { QueueExclusionTable } from "@/features/crawl-workflows/queue-exclusion-table";
import { columns, type Cols } from "@/layouts/columns"; import { columns, type Cols } from "@/layouts/columns";
import infoTextStrings from "@/strings/crawl-workflows/infoText"; import infoTextStrings from "@/strings/crawl-workflows/infoText";
@ -25,7 +28,7 @@ import {
} from "@/utils/workflow"; } from "@/utils/workflow";
type FieldName = keyof FormState; type FieldName = keyof FormState;
type Field = Record<FieldName, TemplateResult<1>>; type Field = Record<FieldName, TemplateResult<1> | undefined>;
const PLACEHOLDER_EXCLUSIONS = [""]; // Add empty slot const PLACEHOLDER_EXCLUSIONS = [""]; // Add empty slot
@ -51,6 +54,9 @@ export class OrgSettingsCrawlWorkflows extends BtrixElement {
} }
`; `;
@consume({ context: proxiesContext, subscribe: true })
private readonly proxies?: ProxiesContext;
@state() @state()
private defaults: WorkflowDefaults = appDefaults; private defaults: WorkflowDefaults = appDefaults;
@ -60,6 +66,9 @@ export class OrgSettingsCrawlWorkflows extends BtrixElement {
@query("btrix-language-select") @query("btrix-language-select")
languageSelect?: LanguageSelect | null; languageSelect?: LanguageSelect | null;
@query("btrix-select-crawler-proxy")
proxySelect?: SelectCrawlerProxy | null;
@query('sl-button[type="submit"]') @query('sl-button[type="submit"]')
submitButton?: SlButton | null; submitButton?: SlButton | null;
@ -195,10 +204,16 @@ export class OrgSettingsCrawlWorkflows extends BtrixElement {
size="small" size="small"
></btrix-select-browser-profile> ></btrix-select-browser-profile>
`, `,
proxyId: html` <btrix-select-crawler-proxy proxyId: this.proxies?.servers.length
orgId=${this.orgId} ? html` <btrix-select-crawler-proxy
.proxyId="${orgDefaults.proxyId || null}" defaultProxyId=${ifDefined(
></btrix-select-crawler-proxy>`, this.proxies.default_proxy_id ?? undefined,
)}
.proxyServers=${this.proxies.servers}
.proxyId="${orgDefaults.proxyId || null}"
size="small"
></btrix-select-crawler-proxy>`
: undefined,
crawlerChannel: html` crawlerChannel: html`
<btrix-select-crawler <btrix-select-crawler
crawlerChannel=${ifDefined(orgDefaults.crawlerChannel)} crawlerChannel=${ifDefined(orgDefaults.crawlerChannel)}
@ -254,10 +269,12 @@ export class OrgSettingsCrawlWorkflows extends BtrixElement {
Object.entries(this.fields).map(([sectionName, fields]) => Object.entries(this.fields).map(([sectionName, fields]) =>
section( section(
sectionName as SectionsEnum, sectionName as SectionsEnum,
Object.entries(fields).map(([fieldName, field]) => [ Object.entries(fields)
field, .filter(([, field]) => field as unknown)
infoTextStrings[fieldName as FieldName], .map(([fieldName, field]) => [
]), field,
infoTextStrings[fieldName as FieldName],
]),
), ),
), ),
)} )}
@ -291,7 +308,7 @@ export class OrgSettingsCrawlWorkflows extends BtrixElement {
blockAds: values.blockAds === "on", blockAds: values.blockAds === "on",
profileid: values.profileid, profileid: values.profileid,
crawlerChannel: values.crawlerChannel, crawlerChannel: values.crawlerChannel,
proxyId: values.proxyId, proxyId: this.proxySelect?.value || undefined,
userAgent: values.userAgent, userAgent: values.userAgent,
lang: this.languageSelect?.value || undefined, lang: this.languageSelect?.value || undefined,
exclude: this.exclusionTable?.exclusions?.filter((v) => v) || [], exclude: this.exclusionTable?.exclusions?.filter((v) => v) || [],

9
frontend/yarn.lock generated
View File

@ -1161,6 +1161,13 @@
resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz#353ce4a76c83fadec272ea5674ede767650762fd" resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz#353ce4a76c83fadec272ea5674ede767650762fd"
integrity sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g== integrity sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==
"@lit/context@^1.1.3":
version "1.1.3"
resolved "https://registry.yarnpkg.com/@lit/context/-/context-1.1.3.tgz#66f8832e57f760f51f39c9d658ca6bd78f809e19"
integrity sha512-Auh37F4S0PZM93HTDfZWs97mmzaQ7M3vnTc9YvxAGyP3UItSK/8Fs0vTOGT+njuvOwbKio/l8Cx/zWL4vkutpQ==
dependencies:
"@lit/reactive-element" "^1.6.2 || ^2.0.0"
"@lit/localize-tools@^0.8.0": "@lit/localize-tools@^0.8.0":
version "0.8.0" version "0.8.0"
resolved "https://registry.yarnpkg.com/@lit/localize-tools/-/localize-tools-0.8.0.tgz#8a14b3961aa63ef801c9f63274cb1d58821a3552" resolved "https://registry.yarnpkg.com/@lit/localize-tools/-/localize-tools-0.8.0.tgz#8a14b3961aa63ef801c9f63274cb1d58821a3552"
@ -1197,7 +1204,7 @@
dependencies: dependencies:
"@lit-labs/ssr-dom-shim" "^1.0.0" "@lit-labs/ssr-dom-shim" "^1.0.0"
"@lit/reactive-element@^1.0.0 || ^2.0.0": "@lit/reactive-element@^1.0.0 || ^2.0.0", "@lit/reactive-element@^1.6.2 || ^2.0.0":
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b" resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b"
integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ== integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==