From 5e856970d169f7c1bdfafab84254f3c01cf626b7 Mon Sep 17 00:00:00 2001 From: sleepyfran Date: Sat, 31 Aug 2024 01:05:35 +0200 Subject: [PATCH] Initial migration to Lit --- .eslintrc.cjs | 8 +- package.json | 9 +- packages/components/add-provider/index.ts | 2 +- packages/components/add-provider/package.json | 16 +- .../add-provider/src/AddProvider.tsx | 117 -------- .../add-provider/src/add-provider.ts | 78 ++++++ .../add-provider/src/provider-loader.ts | 80 ++++++ .../add-provider/src/select-root.ts | 65 +++++ packages/components/library/index.ts | 1 - packages/components/library/src/Library.tsx | 39 --- packages/components/provider-status/index.ts | 2 +- .../components/provider-status/package.json | 15 +- .../provider-status/src/ProviderStatus.tsx | 33 --- .../provider-status/src/provider-status.ts | 38 +++ .../components/shared-controllers/index.ts | 1 + .../shared-controllers/package.json | 16 ++ .../src/stream-effect.controller.ts | 105 +++++++ .../src/vite-env.d.ts | 0 .../tsconfig.json | 0 .../workflows/add-provider.workflow.ts | 6 +- .../src/add-provider.machine.ts | 14 +- packages/services/bootstrap-runtime/index.ts | 1 + .../bootstrap-runtime}/package.json | 14 +- .../services/bootstrap-runtime/src/runtime.ts | 29 ++ .../bootstrap-runtime/src/vite-env.d.ts | 0 .../services/bootstrap-runtime/tsconfig.json | 7 + packages/web/index.html | 4 +- packages/web/package.json | 15 +- packages/web/src/App.tsx | 36 --- packages/web/src/main.ts | 43 +++ packages/web/src/main.tsx | 12 - tools/plop-templates/components/generator.cjs | 10 - .../components/template/component.tsx.hbs | 8 - .../components/template/index.ts.hbs | 19 +- .../components/template/package.json.hbs | 14 +- tsconfig.json | 3 +- tsconfig.node.json | 5 +- vite.config.ts | 7 - yarn.lock | 263 +++++++----------- 39 files changed, 630 insertions(+), 505 deletions(-) delete mode 100644 packages/components/add-provider/src/AddProvider.tsx create mode 100644 packages/components/add-provider/src/add-provider.ts create mode 100644 packages/components/add-provider/src/provider-loader.ts create mode 100644 packages/components/add-provider/src/select-root.ts delete mode 100644 packages/components/library/index.ts delete mode 100644 packages/components/library/src/Library.tsx delete mode 100644 packages/components/provider-status/src/ProviderStatus.tsx create mode 100644 packages/components/provider-status/src/provider-status.ts create mode 100644 packages/components/shared-controllers/index.ts create mode 100644 packages/components/shared-controllers/package.json create mode 100644 packages/components/shared-controllers/src/stream-effect.controller.ts rename packages/components/{library => shared-controllers}/src/vite-env.d.ts (100%) rename packages/components/{library => shared-controllers}/tsconfig.json (100%) create mode 100644 packages/services/bootstrap-runtime/index.ts rename packages/{components/library => services/bootstrap-runtime}/package.json (52%) create mode 100644 packages/services/bootstrap-runtime/src/runtime.ts rename tools/plop-templates/components/template/vite-env.d.ts.hbs => packages/services/bootstrap-runtime/src/vite-env.d.ts (100%) create mode 100644 packages/services/bootstrap-runtime/tsconfig.json delete mode 100644 packages/web/src/App.tsx create mode 100644 packages/web/src/main.ts delete mode 100644 packages/web/src/main.tsx delete mode 100644 tools/plop-templates/components/template/component.tsx.hbs delete mode 100644 vite.config.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6c5a46a..c63b998 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -4,13 +4,13 @@ module.exports = { extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:react-hooks/recommended", "plugin:import/recommended", "plugin:import/typescript", + "plugin:wc/recommended", + "plugin:lit/recommended", ], ignorePatterns: ["dist", ".eslintrc.cjs"], parser: "@typescript-eslint/parser", - plugins: ["react-refresh"], settings: { "import/extensions": [".ts", ".tsx"], "import/resolver": { @@ -19,10 +19,6 @@ module.exports = { }, }, rules: { - "react-refresh/only-export-components": [ - "warn", - { allowConstantExport: true }, - ], "@typescript-eslint/no-unused-vars": [ "error", { diff --git a/package.json b/package.json index 3bfad80..e75c394 100644 --- a/package.json +++ b/package.json @@ -16,20 +16,17 @@ }, "dependencies": { "@effect/schema": "^0.71.1", - "effect": "^3.6.5", - "react": "^18.2.0", - "react-dom": "^18.2.0" + "effect": "^3.6.5" }, "devDependencies": { "@rollup/plugin-inject": "^5.0.5", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", - "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^8.57.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.1", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.6", + "eslint-plugin-lit": "^1.14.0", + "eslint-plugin-wc": "^2.1.1", "husky": "~9.0.11", "plop": "^4.0.1", "prettier": "^3.2.5", diff --git a/packages/components/add-provider/index.ts b/packages/components/add-provider/index.ts index 4a9db09..c739b44 100644 --- a/packages/components/add-provider/index.ts +++ b/packages/components/add-provider/index.ts @@ -1 +1 @@ -export * from "./src/AddProvider"; +export * from "./src/add-provider"; diff --git a/packages/components/add-provider/package.json b/packages/components/add-provider/package.json index bf5fceb..dad9a71 100644 --- a/packages/components/add-provider/package.json +++ b/packages/components/add-provider/package.json @@ -3,19 +3,15 @@ "private": true, "version": "1.0.0", "scripts": { - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", "typecheck": "tsc --noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", - "@echo/services-bootstrap": "^1.0.0", - "@echo/services-add-provider-workflow": "^1.0.0", - "@effect-rx/rx": "^0.33.8", - "@effect-rx/rx-react": "^0.30.11", - "effect": "^3.6.5" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22" + "@echo/components-provider-status": "^1.0.0", + "@echo/services-bootstrap-runtime": "^1.0.0", + "effect": "^3.6.5", + "lit": "^3.2.0", + "@lit/task": "^1.0.1" } } \ No newline at end of file diff --git a/packages/components/add-provider/src/AddProvider.tsx b/packages/components/add-provider/src/AddProvider.tsx deleted file mode 100644 index b46fb69..0000000 --- a/packages/components/add-provider/src/AddProvider.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import { - AddProviderWorkflow, - AvailableProviders, - type FolderMetadata, - type ProviderMetadata, -} from "@echo/core-types"; -import { AddProviderWorkflowLive } from "@echo/services-add-provider-workflow"; -import { MainLive } from "@echo/services-bootstrap"; -import { Rx } from "@effect-rx/rx"; -import { useRx } from "@effect-rx/rx-react"; -import { Layer, Match } from "effect"; -import { useCallback, useEffect } from "react"; - -const runtime = Rx.runtime( - AddProviderWorkflowLive.pipe(Layer.provide(MainLive)), -); -const activeProvidersFn = runtime.fn(() => AddProviderWorkflow.activeProviders); -const loadProviderFn = runtime.fn(AddProviderWorkflow.loadProvider); -const connectToProviderFn = runtime.fn(AddProviderWorkflow.connectToProvider); -const selectRootFn = runtime.fn(AddProviderWorkflow.selectRoot); - -export const AddProvider = () => { - const [activeProvidersStatus, loadActiveProviders] = useRx(activeProvidersFn); - const [loadStatus, loadProvider] = useRx(loadProviderFn); - - useEffect(loadActiveProviders, [loadActiveProviders]); - - const onProviderSelected = useCallback( - (metadata: ProviderMetadata) => loadProvider(metadata), - [loadProvider], - ); - - return Match.value(activeProvidersStatus).pipe( - Match.tag("Initial", () =>

Loading...

), - Match.tag("Success", ({ value: activeProviders }) => - Match.value(loadStatus).pipe( - Match.tag("Initial", () => ( - - )), - Match.tag("Success", () => ), - Match.tag("Failure", () => ( -
Failed to load provider.
- )), - Match.exhaustive, - ), - ), - Match.tag("Failure", (error) => ( -
Failed to retrieve current providers: {JSON.stringify(error)}
- )), - Match.exhaustive, - ); -}; - -const ProviderSelector = ({ - currentlyActiveProviders, - onProviderSelected, -}: { - currentlyActiveProviders: ProviderMetadata[]; - onProviderSelected: (metadata: ProviderMetadata) => void; -}) => - AvailableProviders.filter( - (m) => !currentlyActiveProviders.some((p) => p.id === m.id), - ).map((metadata) => ( - - )); - -const ProviderAuthenticator = () => { - const [connectionStatus, connectToProvider] = useRx(connectToProviderFn); - const _connectToProvider = () => connectToProvider(); - - return ( -
- {Match.value(connectionStatus).pipe( - Match.tag("Initial", () => ( - - )), - Match.tag("Success", ({ value: rootFolderContent }) => ( - - )), - Match.tag("Failure", (error) => ( -
Error: {JSON.stringify(error)}
- )), - Match.exhaustive, - )} -
- ); -}; - -const SelectRoot = ({ - rootFolderContent: folders, -}: { - rootFolderContent: FolderMetadata[]; -}) => { - const [selectRootStatus, selectRoot] = useRx(selectRootFn); - - return Match.value(selectRootStatus).pipe( - Match.tag("Initial", () => - folders.map((folder) => ( - - )), - ), - Match.tag("Success", () =>

Done!

), - Match.tag("Failure", () => ( -

- Uh, oh! Something went wrong. Check the console :( -

- )), - Match.exhaustive, - ); -}; diff --git a/packages/components/add-provider/src/add-provider.ts b/packages/components/add-provider/src/add-provider.ts new file mode 100644 index 0000000..cccf5c7 --- /dev/null +++ b/packages/components/add-provider/src/add-provider.ts @@ -0,0 +1,78 @@ +import { + AddProviderWorkflow, + type FolderMetadata, + type ProviderMetadata, +} from "@echo/core-types"; +import { Task } from "@lit/task"; +import { LitElement, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; +import { getOrCreateRuntime } from "@echo/services-bootstrap-runtime"; +import type { ProviderLoadedEvent } from "./provider-loader"; +import "@echo/components-provider-status"; +import "./provider-loader"; +import "./select-root"; + +type ProviderStatus = + | { _tag: "LoadingProviders" } + | { _tag: "ProvidersLoaded"; availableProviders: ProviderMetadata[] } + | { _tag: "WaitingForRootFolderSelection"; folders: FolderMetadata[] } + | { _tag: "ProviderStarted" }; + +/** + * Component that displays a list of providers that can be added to the application + * and allows the user to select one. + */ +@customElement("add-provider") +export class AddProvider extends LitElement { + @property() + private _providerStatus: ProviderStatus = { _tag: "LoadingProviders" }; + + // @ts-expect-error "Task executes automatically" + private _availableProvidersTask = new Task(this, { + task: () => + getOrCreateRuntime() + .runPromise(AddProviderWorkflow.availableProviders) + .then((availableProviders) => { + this._providerStatus = { + _tag: "ProvidersLoaded", + availableProviders, + }; + }), + args: () => [], + }); + + render() { + return this._providerStatus._tag === "LoadingProviders" + ? html`

Loading providers...

` + : this._providerStatus._tag === "ProvidersLoaded" + ? html`` + : this._providerStatus._tag === "WaitingForRootFolderSelection" + ? html`` + : html``; + } + + private _onProviderLoaded(event: ProviderLoadedEvent) { + this._providerStatus = { + _tag: "WaitingForRootFolderSelection", + folders: event.availableFolders, + }; + } + + private _onRootSelected() { + this._providerStatus = { + _tag: "ProviderStarted", + }; + } +} + +declare global { + interface HTMLElementTagNameMap { + "add-provider": AddProvider; + } +} diff --git a/packages/components/add-provider/src/provider-loader.ts b/packages/components/add-provider/src/provider-loader.ts new file mode 100644 index 0000000..ae7742b --- /dev/null +++ b/packages/components/add-provider/src/provider-loader.ts @@ -0,0 +1,80 @@ +import { + AddProviderWorkflow, + type FolderMetadata, + type ProviderMetadata, +} from "@echo/core-types"; +import { getOrCreateRuntime } from "@echo/services-bootstrap-runtime"; +import { Task } from "@lit/task"; +import { LitElement, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +/** + * Event that gets dispatched by the component when the provider has been loaded + * and is awaiting root folder selection. + */ +export class ProviderLoadedEvent extends Event { + constructor(public availableFolders: FolderMetadata[]) { + super("provider-loaded", { bubbles: true, composed: true }); + } +} + +/** + * Component that displays a list of available providers and loads them upon selection. + */ +@customElement("provider-loader") +export class ProviderLoader extends LitElement { + @property({ type: Array }) + availableProviders: ProviderMetadata[] = []; + + private _loadProvider = new Task(this, { + task: ([provider]: [ProviderMetadata]) => + getOrCreateRuntime().runPromise( + AddProviderWorkflow.loadProvider(provider), + ), + autoRun: false, + }); + + private _connectToProvider = new Task(this, { + task: () => + getOrCreateRuntime().runPromise(AddProviderWorkflow.connectToProvider()), + autoRun: false, + }); + + // @ts-expect-error "Task executes automatically" + private _notifyProviderLoaded = new Task(this, { + args: () => [this._connectToProvider.value], + task: ([rootFolder]) => { + if (rootFolder) { + this.dispatchEvent(new ProviderLoadedEvent(rootFolder)); + } + }, + }); + + render() { + return this._connectToProvider.render({ + initial: () => + this._loadProvider.render({ + initial: () => + this.availableProviders.map( + (provider) => html` + + `, + ), + complete: (providerMetadata) => + html``, + }), + pending: () => html`

Connecting...

`, + complete: () => html`

Connected!

`, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "provider-loader": ProviderLoader; + } +} diff --git a/packages/components/add-provider/src/select-root.ts b/packages/components/add-provider/src/select-root.ts new file mode 100644 index 0000000..a2b7842 --- /dev/null +++ b/packages/components/add-provider/src/select-root.ts @@ -0,0 +1,65 @@ +import { AddProviderWorkflow, type FolderMetadata } from "@echo/core-types"; +import { getOrCreateRuntime } from "@echo/services-bootstrap-runtime"; +import { Task } from "@lit/task"; +import { LitElement, html, nothing } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +/** + * Event that gets dispatched by the component when the root has been selected + * and the provider has started successfully. + */ +export class ProviderStartedEvent extends Event { + constructor() { + super("root-selected", { bubbles: true, composed: true }); + } +} + +/** + * Component that displays a list of available folders and allows the user to + * select one as the root, which will start the provider upon selection. + */ +@customElement("select-root") +export class SelectRoot extends LitElement { + @property({ type: Array }) + availableFolders: FolderMetadata[] = []; + + private _selectRoot = new Task(this, { + task: ([rootFolder]: [FolderMetadata]) => + getOrCreateRuntime().runPromise( + AddProviderWorkflow.selectRoot(rootFolder), + ), + autoRun: false, + }); + + // @ts-expect-error "Task executes automatically" + private _notifyProviderStarted = new Task(this, { + args: () => [this._selectRoot.value], + task: ([completed]) => { + if (completed !== undefined) { + this.dispatchEvent(new ProviderStartedEvent()); + } + }, + }); + + render() { + return this._selectRoot.render({ + initial: () => html` +

Select a root folder:

+ ${this.availableFolders.map( + (folder) => + html``, + )} + `, + pending: () => html`

Connecting...

`, + complete: () => nothing, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "select-root": SelectRoot; + } +} diff --git a/packages/components/library/index.ts b/packages/components/library/index.ts deleted file mode 100644 index 5d080ec..0000000 --- a/packages/components/library/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserLibraryWithSuspense as UserLibrary } from "./src/Library"; diff --git a/packages/components/library/src/Library.tsx b/packages/components/library/src/Library.tsx deleted file mode 100644 index dd1a6ac..0000000 --- a/packages/components/library/src/Library.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { Library, Player } from "@echo/core-types"; -import { MainLive } from "@echo/services-bootstrap"; -import { Rx } from "@effect-rx/rx"; -import { Layer, Stream } from "effect"; -import { Suspense } from "react"; -import { LibraryLive } from "@echo/services-library"; -import { PlayerLive } from "@echo/services-player"; -import { useRx, useRxSuspenseSuccess } from "@effect-rx/rx-react"; - -const runtime = Rx.runtime( - Layer.mergeAll(LibraryLive, PlayerLive).pipe(Layer.provide(MainLive)), -); -const observeLibrary = runtime.rx(Stream.unwrap(Library.observeAlbums())); -const playAlbumFn = runtime.fn(Player.playAlbum); - -const UserLibrary = () => { - const albums = useRxSuspenseSuccess(observeLibrary).value; - const [, playAlbum] = useRx(playAlbumFn); - - return ( -
-
- {albums.map((album) => ( -
-

{album.name}

-

{album.artist.name}

- -
-
- ))} -
- ); -}; - -export const UserLibraryWithSuspense = () => ( - - - -); diff --git a/packages/components/provider-status/index.ts b/packages/components/provider-status/index.ts index 15c98dc..79ea09d 100644 --- a/packages/components/provider-status/index.ts +++ b/packages/components/provider-status/index.ts @@ -1 +1 @@ -export * from "./src/ProviderStatus"; +export * from "./src/provider-status"; diff --git a/packages/components/provider-status/package.json b/packages/components/provider-status/package.json index 661ea0a..9529df5 100644 --- a/packages/components/provider-status/package.json +++ b/packages/components/provider-status/package.json @@ -3,18 +3,15 @@ "private": true, "version": "1.0.0", "scripts": { - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", "typecheck": "tsc --noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", - "@echo/services-bootstrap": "^1.0.0", - "@effect-rx/rx": "^0.33.8", - "@effect-rx/rx-react": "^0.30.11", - "effect": "^3.6.5" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22" + "@echo/components-shared-controllers": "^1.0.0", + "@echo/services-bootstrap-runtime": "^1.0.0", + "effect": "^3.6.5", + "lit": "^3.2.0", + "@lit/task": "^1.0.1" } } \ No newline at end of file diff --git a/packages/components/provider-status/src/ProviderStatus.tsx b/packages/components/provider-status/src/ProviderStatus.tsx deleted file mode 100644 index 568ee3f..0000000 --- a/packages/components/provider-status/src/ProviderStatus.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { MainLive } from "@echo/services-bootstrap"; -import { Rx } from "@effect-rx/rx"; -import { Match } from "effect"; -import { useRxValue } from "@effect-rx/rx-react"; -import { MediaProviderStatus } from "@echo/core-types"; - -const runtime = Rx.runtime(MainLive); - -const providerStatus = runtime.subscriptionRef(MediaProviderStatus.observe); - -export const ProviderStatus = () => { - const status = useRxValue(providerStatus); - - return Match.value(status).pipe( - Match.tag("Initial", () =>
Loading provider status...
), - Match.tag("Success", ({ value: providerState }) => ( -
- {[...providerState.entries()].map(([providerId, providerState]) => ( -
-

{providerId}

-
{JSON.stringify(providerState, null, 2)}
-
- ))} -
- )), - Match.tag("Failure", () => ( -
- Something went wrong observing the provider statuses. -
- )), - Match.exhaustive, - ); -}; diff --git a/packages/components/provider-status/src/provider-status.ts b/packages/components/provider-status/src/provider-status.ts new file mode 100644 index 0000000..2d8832f --- /dev/null +++ b/packages/components/provider-status/src/provider-status.ts @@ -0,0 +1,38 @@ +import { MediaProviderStatus } from "@echo/core-types"; +import { StreamEffectController } from "@echo/components-shared-controllers"; +import { LitElement, html } from "lit"; +import { customElement } from "lit/decorators.js"; +import { map } from "lit/directives/map.js"; + +/** + * Component that displays the status of all active providers. + */ +@customElement("provider-status") +export class ProviderStatus extends LitElement { + private _providerStatus = new StreamEffectController( + this, + MediaProviderStatus.observe, + ); + + render() { + return this._providerStatus.render({ + initial: () => html`

Retrieving syncing status...

`, + item: (status) => + map( + status, + ([providerId, providerStatus]) => html` +

${providerId}

+
${JSON.stringify(providerStatus, null, 2)}
+ `, + ), + complete: () => html`

Complete

`, + error: (error) => html`

Error: ${error}

`, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "provider-status": ProviderStatus; + } +} diff --git a/packages/components/shared-controllers/index.ts b/packages/components/shared-controllers/index.ts new file mode 100644 index 0000000..0aee6d0 --- /dev/null +++ b/packages/components/shared-controllers/index.ts @@ -0,0 +1 @@ +export * from "./src/stream-effect.controller"; diff --git a/packages/components/shared-controllers/package.json b/packages/components/shared-controllers/package.json new file mode 100644 index 0000000..aef3b90 --- /dev/null +++ b/packages/components/shared-controllers/package.json @@ -0,0 +1,16 @@ +{ + "name": "@echo/components-shared-controllers", + "private": true, + "version": "1.0.0", + "scripts": { + "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@echo/core-types": "^1.0.0", + "@echo/services-bootstrap-runtime": "^1.0.0", + "effect": "^3.6.5", + "lit": "^3.2.0", + "@lit/task": "^1.0.1" + } +} \ No newline at end of file diff --git a/packages/components/shared-controllers/src/stream-effect.controller.ts b/packages/components/shared-controllers/src/stream-effect.controller.ts new file mode 100644 index 0000000..f68c8b0 --- /dev/null +++ b/packages/components/shared-controllers/src/stream-effect.controller.ts @@ -0,0 +1,105 @@ +import { + getOrCreateRuntime, + type EchoRuntimeServices, +} from "@echo/services-bootstrap-runtime"; +import { Effect, Stream, type SubscriptionRef } from "effect"; +import type { ReactiveController, ReactiveControllerHost } from "lit"; + +/** + * Defines a renderer that can render each different status of a stream. + */ +export type StatusRenderer = { + /** + * Called when the stream has not yet produced an item. + */ + initial?: () => unknown; + + /** + * Called when the stream produces an item. + */ + item?: (item: A) => unknown; + + /** + * Called when the stream finishes. + */ + complete?: () => unknown; + + /** + * Called when the stream errors. + */ + error?: (error: E) => unknown; +}; + +type StreamStatus = + | { _tag: "Initial" } + | { _tag: "Item"; item: A } + | { _tag: "Complete" } + | { _tag: "Error"; error: E }; + +const isSubscriptionRef = ( + streamOrRef: Stream.Stream | SubscriptionRef.SubscriptionRef, +): streamOrRef is SubscriptionRef.SubscriptionRef => + "changes" in streamOrRef; + +/** + * Controller that takes an effect that produces a stream or a subscription ref + * and exposes a render method that renders maps the different states of the + * stream to a renderer. + */ +export class StreamEffectController implements ReactiveController { + private host: ReactiveControllerHost; + private _status: StreamStatus = { _tag: "Initial" }; + + constructor( + host: ReactiveControllerHost, + private readonly _streamEffect: Effect.Effect< + Stream.Stream | SubscriptionRef.SubscriptionRef, + never, + EchoRuntimeServices + >, + ) { + (this.host = host).addController(this); + } + + hostConnected(): void { + const consumer$ = this._streamEffect.pipe( + Effect.flatMap((streamOrRef) => { + const stream = isSubscriptionRef(streamOrRef) + ? streamOrRef.changes + : streamOrRef; + return stream.pipe( + Stream.tap((item) => this.handleUpdate$({ _tag: "Item", item })), + Stream.tapError((error) => + this.handleUpdate$({ _tag: "Error", error }), + ), + Stream.runDrain, + ); + }), + ); + + getOrCreateRuntime().runPromise(consumer$); + } + + /** + * Maps the different states of the stream to a renderer. + */ + render(renderer: StatusRenderer) { + switch (this._status._tag) { + case "Initial": + return renderer.initial?.(); + case "Item": + return renderer.item?.(this._status.item); + case "Complete": + return renderer.complete?.(); + case "Error": + return renderer.error?.(this._status.error); + } + } + + private handleUpdate$(state: StreamStatus) { + return Effect.sync(() => { + this._status = state; + this.host.requestUpdate(); + }); + } +} diff --git a/packages/components/library/src/vite-env.d.ts b/packages/components/shared-controllers/src/vite-env.d.ts similarity index 100% rename from packages/components/library/src/vite-env.d.ts rename to packages/components/shared-controllers/src/vite-env.d.ts diff --git a/packages/components/library/tsconfig.json b/packages/components/shared-controllers/tsconfig.json similarity index 100% rename from packages/components/library/tsconfig.json rename to packages/components/shared-controllers/tsconfig.json diff --git a/packages/core/types/src/services/workflows/add-provider.workflow.ts b/packages/core/types/src/services/workflows/add-provider.workflow.ts index 1ea386e..f43ea26 100644 --- a/packages/core/types/src/services/workflows/add-provider.workflow.ts +++ b/packages/core/types/src/services/workflows/add-provider.workflow.ts @@ -15,8 +15,10 @@ export type Empty = Record; * Workflow that orchestrates the process of adding a new provider to the application. */ export type IAddProviderWorkflow = { - readonly activeProviders: Effect.Effect; - readonly loadProvider: (metadata: ProviderMetadata) => Effect.Effect; + readonly availableProviders: Effect.Effect; + readonly loadProvider: ( + metadata: ProviderMetadata, + ) => Effect.Effect; readonly connectToProvider: () => Effect.Effect< FolderMetadata[], AuthenticationError | FileBasedProviderError diff --git a/packages/services/add-provider-workflow/src/add-provider.machine.ts b/packages/services/add-provider-workflow/src/add-provider.machine.ts index 8257bec..b18be16 100644 --- a/packages/services/add-provider-workflow/src/add-provider.machine.ts +++ b/packages/services/add-provider-workflow/src/add-provider.machine.ts @@ -3,6 +3,7 @@ import * as Machine from "@effect/experimental/Machine"; import { ActiveMediaProviderCache, AddProviderWorkflow, + AvailableProviders, LocalStorage, MediaProviderMainThreadBroadcastChannel, type Authentication, @@ -21,7 +22,7 @@ import { } from "@echo/services-bootstrap"; class LoadProvider extends Request.TaggedClass("LoadProvider")< - Empty, + ProviderMetadata, never, { readonly metadata: ProviderMetadata; @@ -81,7 +82,7 @@ export const addProviderWorkflow = Machine.makeWith()( ({ state, request }) => Effect.gen(function* () { if (state._tag !== "Idle") { - return [{}, state]; + return [request.metadata, state]; } const providerFactory = yield* providerLazyLoader.load( @@ -92,7 +93,7 @@ export const addProviderWorkflow = Machine.makeWith()( ); return [ - {}, + request.metadata, { _tag: "WaitingForConnection" as const, loadedProvider: { @@ -185,8 +186,13 @@ export const AddProviderWorkflowLive = Layer.scoped( const activeMediaProviderCache = yield* ActiveMediaProviderCache; return { - activeProviders: activeMediaProviderCache.getAll.pipe( + availableProviders: activeMediaProviderCache.getAll.pipe( Effect.map((providers) => providers.map((p) => p.metadata)), + Effect.map((allActiveProviders) => + AvailableProviders.filter( + (provider) => !allActiveProviders.some((p) => p.id === provider.id), + ), + ), ), loadProvider: (metadata) => actor.send(new LoadProvider({ metadata })), connectToProvider: () => actor.send(new ConnectToProvider({})), diff --git a/packages/services/bootstrap-runtime/index.ts b/packages/services/bootstrap-runtime/index.ts new file mode 100644 index 0000000..433061b --- /dev/null +++ b/packages/services/bootstrap-runtime/index.ts @@ -0,0 +1 @@ +export * from "./src/runtime"; diff --git a/packages/components/library/package.json b/packages/services/bootstrap-runtime/package.json similarity index 52% rename from packages/components/library/package.json rename to packages/services/bootstrap-runtime/package.json index 715d0b1..30032f0 100644 --- a/packages/components/library/package.json +++ b/packages/services/bootstrap-runtime/package.json @@ -1,22 +1,18 @@ { - "name": "@echo/components-library", + "name": "@echo/services-bootstrap-runtime", "private": true, "version": "1.0.0", + "description": "Contains the implementation for the BootstrapRuntime service", + "main": "index.js", "scripts": { "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", "typecheck": "tsc --noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", + "@echo/services-add-provider-workflow": "^1.0.0", + "@echo/services-app-init": "^1.0.0", "@echo/services-bootstrap": "^1.0.0", - "@echo/services-library": "^1.0.0", - "@echo/services-player": "^1.0.0", - "@effect-rx/rx": "^0.33.8", - "@effect-rx/rx-react": "^0.30.11", "effect": "^3.6.5" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22" } } \ No newline at end of file diff --git a/packages/services/bootstrap-runtime/src/runtime.ts b/packages/services/bootstrap-runtime/src/runtime.ts new file mode 100644 index 0000000..1008c25 --- /dev/null +++ b/packages/services/bootstrap-runtime/src/runtime.ts @@ -0,0 +1,29 @@ +import { AddProviderWorkflowLive } from "@echo/services-add-provider-workflow"; +import { AppInitLive } from "@echo/services-app-init"; +import { MainLive } from "@echo/services-bootstrap"; +import { Layer, ManagedRuntime } from "effect"; +import { globalValue } from "effect/GlobalValue"; + +/** + * Runtime for the application that exposes the services that can be used + * from the UI layer. + */ +export const getOrCreateRuntime = () => + globalValue("echo-runtime", () => + ManagedRuntime.make( + Layer.mergeAll(AppInitLive, AddProviderWorkflowLive).pipe( + Layer.provideMerge(MainLive), + ), + ), + ); + +/** + * Type that represents the runtime that is available in the application. + */ +export type EchoRuntime = ReturnType; + +/** + * Type that represents the services that are available in the runtime. + */ +export type EchoRuntimeServices = + TRuntime extends ManagedRuntime.ManagedRuntime ? A : never; diff --git a/tools/plop-templates/components/template/vite-env.d.ts.hbs b/packages/services/bootstrap-runtime/src/vite-env.d.ts similarity index 100% rename from tools/plop-templates/components/template/vite-env.d.ts.hbs rename to packages/services/bootstrap-runtime/src/vite-env.d.ts diff --git a/packages/services/bootstrap-runtime/tsconfig.json b/packages/services/bootstrap-runtime/tsconfig.json new file mode 100644 index 0000000..6953ff5 --- /dev/null +++ b/packages/services/bootstrap-runtime/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../../tsconfig.json", + "include": [ + "src", + "index.ts" + ] +} \ No newline at end of file diff --git a/packages/web/index.html b/packages/web/index.html index 0d29c3b..b5cb81d 100644 --- a/packages/web/index.html +++ b/packages/web/index.html @@ -4,10 +4,10 @@ + 🎧 Echo -
- + diff --git a/packages/web/package.json b/packages/web/package.json index 27c1878..1b6ebcc 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -12,18 +12,11 @@ }, "dependencies": { "@echo/components-add-provider": "^1.0.0", - "@echo/components-library": "^1.0.0", - "@echo/components-provider-status": "^1.0.0", "@echo/core-types": "^1.0.0", - "@echo/services-app-init": "^1.0.0", - "@echo/services-bootstrap": "^1.0.0", + "@echo/services-bootstrap-runtime": "^1.0.0", "@echo/services-bootstrap-workers": "^1.0.0", - "@effect-rx/rx": "0.33.8", - "@effect-rx/rx-react": "0.30.11", - "effect": "^3.6.5" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22" + "effect": "^3.6.5", + "lit": "^3.2.0", + "@lit/task": "^1.0.1" } } \ No newline at end of file diff --git a/packages/web/src/App.tsx b/packages/web/src/App.tsx deleted file mode 100644 index 55e387c..0000000 --- a/packages/web/src/App.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { AddProvider } from "@echo/components-add-provider"; -import { UserLibrary } from "@echo/components-library"; -import { ProviderStatus } from "@echo/components-provider-status"; -import { AppInit } from "@echo/core-types"; -import { AppInitLive } from "@echo/services-app-init"; -import { MainLive } from "@echo/services-bootstrap"; -import { Rx } from "@effect-rx/rx"; -import { useRx } from "@effect-rx/rx-react"; -import { Layer, Match } from "effect"; -import { useEffect } from "react"; - -const runtime = Rx.runtime(AppInitLive.pipe(Layer.provide(MainLive))); -const appInitRx = runtime.fn(() => AppInit.init); - -export const App = () => { - const [initStatus, init] = useRx(appInitRx); - - useEffect(init, [init]); - - return Match.value(initStatus).pipe( - Match.tag("Initial", () =>

Initializing Echo...

), - Match.tag("Success", () => ( -
- - - -
- )), - Match.tag("Failure", () => ( -

- Ooops, something went wrong. Please report it! -

- )), - Match.exhaustive, - ); -}; diff --git a/packages/web/src/main.ts b/packages/web/src/main.ts new file mode 100644 index 0000000..c5e0b3e --- /dev/null +++ b/packages/web/src/main.ts @@ -0,0 +1,43 @@ +import { LitElement, html } from "lit"; +import { customElement } from "lit/decorators.js"; +import { initializeWorkers } from "@echo/services-bootstrap-workers"; +import { getOrCreateRuntime } from "@echo/services-bootstrap-runtime"; +import { Task } from "@lit/task"; +import { AppInit } from "@echo/core-types"; +import "@echo/components-add-provider"; + +initializeWorkers(); + +/** + * Root element of the application. + */ +@customElement("app-root") +export class MyElement extends LitElement { + private _initTask = new Task(this, { + task: () => getOrCreateRuntime().runPromise(AppInit.init), + args: () => [], + }); + + render() { + return this._initTask.render({ + initial: () => html`

Initializing Echo...

`, + complete: () => html` +
+ + + +
+ `, + error: () => + html`

+ Ooops, something went wrong. Please report it! +

`, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "app-root": MyElement; + } +} diff --git a/packages/web/src/main.tsx b/packages/web/src/main.tsx deleted file mode 100644 index cb55fd9..0000000 --- a/packages/web/src/main.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import React from "react"; -import { createRoot } from "react-dom/client"; -import { App } from "./App"; -import { initializeWorkers } from "@echo/services-bootstrap-workers"; - -initializeWorkers(); - -createRoot(document.getElementById("root")!).render( - - - , -); diff --git a/tools/plop-templates/components/generator.cjs b/tools/plop-templates/components/generator.cjs index 16db9d7..46000d7 100644 --- a/tools/plop-templates/components/generator.cjs +++ b/tools/plop-templates/components/generator.cjs @@ -17,11 +17,6 @@ module.exports = { path: "packages/components/{{dashCase name}}/index.ts", templateFile: `${__dirname}/template/index.ts.hbs`, }, - { - type: "add", - path: "packages/components/{{dashCase name}}/src/{{properCase name}}.tsx", - templateFile: `${__dirname}/template/component.tsx.hbs`, - }, { type: "add", path: "packages/components/{{dashCase name}}/package.json", @@ -32,10 +27,5 @@ module.exports = { path: "packages/components/{{dashCase name}}/tsconfig.json", templateFile: `${__dirname}/template/tsconfig.json.hbs`, }, - { - type: "add", - path: "packages/components/{{dashCase name}}/src/vite-env.d.ts", - templateFile: `${__dirname}/template/vite-env.d.ts.hbs`, - }, ], }; diff --git a/tools/plop-templates/components/template/component.tsx.hbs b/tools/plop-templates/components/template/component.tsx.hbs deleted file mode 100644 index 7a97b1c..0000000 --- a/tools/plop-templates/components/template/component.tsx.hbs +++ /dev/null @@ -1,8 +0,0 @@ -type {{properCase name}}Props = { - /** Something something */ - title: string -} - -export const {{properCase name}} = () => ( -

Welcome!

-); diff --git a/tools/plop-templates/components/template/index.ts.hbs b/tools/plop-templates/components/template/index.ts.hbs index cc21797..f32631e 100644 --- a/tools/plop-templates/components/template/index.ts.hbs +++ b/tools/plop-templates/components/template/index.ts.hbs @@ -1 +1,18 @@ -export * from "./src/{{ properCase name }}"; +import { LitElement, html } from "lit"; +import { customElement } from "lit/decorators.js"; + +/** + * Description of what the {{properCase name}} component does. + */ +@customElement("{{dashCase name}}") +export class {{pascalCase name}} extends LitElement { + render() { + return html`

{{pascalCase name}}

`; + } +} + +declare global { + interface HTMLElementTagNameMap { + "{{dashCase name}}": {{pascalCase name}}; + } +} diff --git a/tools/plop-templates/components/template/package.json.hbs b/tools/plop-templates/components/template/package.json.hbs index 766a277..63c0d19 100644 --- a/tools/plop-templates/components/template/package.json.hbs +++ b/tools/plop-templates/components/template/package.json.hbs @@ -3,18 +3,14 @@ "private": true, "version": "1.0.0", "scripts": { - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0", "typecheck": "tsc --noEmit" }, "dependencies": { "@echo/core-types": "^1.0.0", - "@echo/services-bootstrap": "^1.0.0", - "@effect-rx/rx": "^0.33.8", - "@effect-rx/rx-react": "^0.30.11", - "effect": "^3.6.5" - }, - "devDependencies": { - "@types/react": "^18.2.66", - "@types/react-dom": "^18.2.22" + "@echo/services-bootstrap-runtime": "^1.0.0", + "effect": "^3.6.5", + "lit": "^3.2.0", + "@lit/task": "^1.0.1" } } diff --git a/tsconfig.json b/tsconfig.json index 8e693a9..d99ea5a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ { "compilerOptions": { "target": "ES2020", - "useDefineForClassFields": true, + "experimentalDecorators": true, + "useDefineForClassFields": false, "lib": [ "ES2020", "DOM", diff --git a/tsconfig.node.json b/tsconfig.node.json index d5ed4ea..3e3f45d 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -6,8 +6,5 @@ "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, "strict": true - }, - "include": [ - "vite.config.ts" - ] + } } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index d366e8c..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineConfig } from "vite"; -import react from "@vitejs/plugin-react-swc"; - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], -}); diff --git a/yarn.lock b/yarn.lock index 97dc103..84b355c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -21,18 +21,6 @@ dependencies: regenerator-runtime "^0.14.0" -"@effect-rx/rx-react@0.30.11", "@effect-rx/rx-react@^0.30.11": - version "0.30.11" - resolved "https://registry.yarnpkg.com/@effect-rx/rx-react/-/rx-react-0.30.11.tgz#af1638e4505acba7c9a30f8a8dd43b99944eccaf" - integrity sha512-Z0HwvrJbzgUvpjgMS5qH2+4dYL0oYKbsx2UJ9S7b1iV8ZippJnLf//FsOqDjzUdXKNLuW1L/h+as70o7DLDrgw== - dependencies: - "@effect-rx/rx" "^0.33.8" - -"@effect-rx/rx@0.33.8", "@effect-rx/rx@^0.33.8": - version "0.33.8" - resolved "https://registry.yarnpkg.com/@effect-rx/rx/-/rx-0.33.8.tgz#90b4076ea29750bc641c6adb1e878fbacc34ba0c" - integrity sha512-dZZW2BbXNRcvNB37tdjWzCZQpz6D+xPaZcaFusQImvwMzMNhqzVHQa9a8V7r8+fZrgzY1nJpM2HypdfGSsjAng== - "@effect/experimental@^0.23.4": version "0.23.4" resolved "https://registry.yarnpkg.com/@effect/experimental/-/experimental-0.23.4.tgz#b44e6d6ba71a467b1978db8309c2c3ace9cb6221" @@ -223,6 +211,25 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@lit-labs/ssr-dom-shim@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz#2f3a8f1d688935c704dbc89132394a41029acbb8" + integrity sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ== + +"@lit/reactive-element@^1.0.0 || ^2.0.0", "@lit/reactive-element@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@lit/reactive-element/-/reactive-element-2.0.4.tgz#8f2ed950a848016383894a26180ff06c56ae001b" + integrity sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + +"@lit/task@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@lit/task/-/task-1.0.1.tgz#7462aeaa973766822567f5ca90fe157404e8eb81" + integrity sha512-fVLDtmwCau8NywnFIXaJxsCZjzaIxnVq+cFRKYC1Y4tA4/0rMTvF6DLZZ2JE51BwzOluaKtgJX8x1QDsQtAaIw== + dependencies: + "@lit/reactive-element" "^1.0.0 || ^2.0.0" + "@ljharb/through@^2.3.13": version "2.3.13" resolved "https://registry.yarnpkg.com/@ljharb/through/-/through-2.3.13.tgz#b7e4766e0b65aa82e529be945ab078de79874edc" @@ -392,87 +399,6 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz#5a2d08b81e8064b34242d5cc9973ef8dd1e60503" integrity sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w== -"@swc/core-darwin-arm64@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.5.7.tgz#2b5cdbd34e4162e50de6147dd1a5cb12d23b08e8" - integrity sha512-bZLVHPTpH3h6yhwVl395k0Mtx8v6CGhq5r4KQdAoPbADU974Mauz1b6ViHAJ74O0IVE5vyy7tD3OpkQxL/vMDQ== - -"@swc/core-darwin-x64@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.5.7.tgz#6aa7e3c01ab8e5e41597f8a24ff24c4e50936a46" - integrity sha512-RpUyu2GsviwTc2qVajPL0l8nf2vKj5wzO3WkLSHAHEJbiUZk83NJrZd1RVbEknIMO7+Uyjh54hEh8R26jSByaw== - -"@swc/core-linux-arm-gnueabihf@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.5.7.tgz#160108633b9e1d1ad05f815bedc7e9eb5d59fc2a" - integrity sha512-cTZWTnCXLABOuvWiv6nQQM0hP6ZWEkzdgDvztgHI/+u/MvtzJBN5lBQ2lue/9sSFYLMqzqff5EHKlFtrJCA9dQ== - -"@swc/core-linux-arm64-gnu@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.5.7.tgz#cbfa512683c73227ad25552f3b3e722b0e7fbd1d" - integrity sha512-hoeTJFBiE/IJP30Be7djWF8Q5KVgkbDtjySmvYLg9P94bHg9TJPSQoC72tXx/oXOgXvElDe/GMybru0UxhKx4g== - -"@swc/core-linux-arm64-musl@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.5.7.tgz#80239cb58fe57f3c86b44617fe784530ec55ee2b" - integrity sha512-+NDhK+IFTiVK1/o7EXdCeF2hEzCiaRSrb9zD7X2Z7inwWlxAntcSuzZW7Y6BRqGQH89KA91qYgwbnjgTQ22PiQ== - -"@swc/core-linux-x64-gnu@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.5.7.tgz#a699c1632de60b6a63b7fdb7abcb4fef317e57ca" - integrity sha512-25GXpJmeFxKB+7pbY7YQLhWWjkYlR+kHz5I3j9WRl3Lp4v4UD67OGXwPe+DIcHqcouA1fhLhsgHJWtsaNOMBNg== - -"@swc/core-linux-x64-musl@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.5.7.tgz#8e4c203d6bc41e7f85d7d34d0fdf4ef751fa626c" - integrity sha512-0VN9Y5EAPBESmSPPsCJzplZHV26akC0sIgd3Hc/7S/1GkSMoeuVL+V9vt+F/cCuzr4VidzSkqftdP3qEIsXSpg== - -"@swc/core-win32-arm64-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.5.7.tgz#31e3d42b8c0aa79f0ea1a980c0dd1a999d378ed7" - integrity sha512-RtoNnstBwy5VloNCvmvYNApkTmuCe4sNcoYWpmY7C1+bPR+6SOo8im1G6/FpNem8AR5fcZCmXHWQ+EUmRWJyuA== - -"@swc/core-win32-ia32-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.5.7.tgz#a235285f9f62850aefcf9abb03420f2c54f63638" - integrity sha512-Xm0TfvcmmspvQg1s4+USL3x8D+YPAfX2JHygvxAnCJ0EHun8cm2zvfNBcsTlnwYb0ybFWXXY129aq1wgFC9TpQ== - -"@swc/core-win32-x64-msvc@1.5.7": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.5.7.tgz#f84641393b5223450d00d97bfff877b8b69d7c9b" - integrity sha512-tp43WfJLCsKLQKBmjmY/0vv1slVywR5Q4qKjF5OIY8QijaEW7/8VwPyUyVoJZEnDgv9jKtUTG5PzqtIYPZGnyg== - -"@swc/core@^1.3.107": - version "1.5.7" - resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.5.7.tgz#e1db7b9887d5f34eb4a3256a738d0c5f1b018c33" - integrity sha512-U4qJRBefIJNJDRCCiVtkfa/hpiZ7w0R6kASea+/KLp+vkus3zcLSB8Ub8SvKgTIxjWpwsKcZlPf5nrv4ls46SQ== - dependencies: - "@swc/counter" "^0.1.2" - "@swc/types" "0.1.7" - optionalDependencies: - "@swc/core-darwin-arm64" "1.5.7" - "@swc/core-darwin-x64" "1.5.7" - "@swc/core-linux-arm-gnueabihf" "1.5.7" - "@swc/core-linux-arm64-gnu" "1.5.7" - "@swc/core-linux-arm64-musl" "1.5.7" - "@swc/core-linux-x64-gnu" "1.5.7" - "@swc/core-linux-x64-musl" "1.5.7" - "@swc/core-win32-arm64-msvc" "1.5.7" - "@swc/core-win32-ia32-msvc" "1.5.7" - "@swc/core-win32-x64-msvc" "1.5.7" - -"@swc/counter@^0.1.2", "@swc/counter@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" - integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== - -"@swc/types@0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.7.tgz#ea5d658cf460abff51507ca8d26e2d391bafb15e" - integrity sha512-scHWahbHF0eyj3JsxG9CFJgFdFNaVQCNAimBlT6PzS3n/HptxqREjsm4OH6AN3lYcffZYSPxXW8ua2BEHp0lJQ== - dependencies: - "@swc/counter" "^0.1.3" - "@tokenizer/token@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" @@ -516,26 +442,6 @@ dependencies: undici-types "~5.26.4" -"@types/prop-types@*": - version "15.7.12" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" - integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== - -"@types/react-dom@^18.2.22": - version "18.3.0" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" - integrity sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg== - dependencies: - "@types/react" "*" - -"@types/react@*", "@types/react@^18.2.66": - version "18.3.2" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.2.tgz#462ae4904973bc212fa910424d901e3d137dbfcd" - integrity sha512-Btgg89dAnqD4vV7R3hlwOxgqobUQKgx3MmrQRi0yYbs/P0ym8XozIAlkqVilPqHQwXs4e9Tf63rrCgl58BcO4w== - dependencies: - "@types/prop-types" "*" - csstype "^3.0.2" - "@types/through@*": version "0.0.33" resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.33.tgz#14ebf599320e1c7851e7d598149af183c6b9ea56" @@ -543,6 +449,11 @@ dependencies: "@types/node" "*" +"@types/trusted-types@^2.0.2": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.7.tgz#baccb07a970b91707df3a3e8ba6896c57ead2d11" + integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== + "@typescript-eslint/eslint-plugin@^7.2.0": version "7.9.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz#093b96fc4e342226e65d5f18f9c87081e0b04a31" @@ -629,13 +540,6 @@ resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== -"@vitejs/plugin-react-swc@^3.5.0": - version "3.6.0" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-swc/-/plugin-react-swc-3.6.0.tgz#dc9cd1363baf3780f3ad3e0a12a46a3ffe0c7526" - integrity sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g== - dependencies: - "@swc/core" "^1.3.107" - acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -983,11 +887,6 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -csstype@^3.0.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" - integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== - data-view-buffer@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" @@ -1327,15 +1226,22 @@ eslint-plugin-import@^2.29.1: semver "^6.3.1" tsconfig-paths "^3.15.0" -eslint-plugin-react-hooks@^4.6.0: - version "4.6.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz#c829eb06c0e6f484b3fbb85a97e57784f328c596" - integrity sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ== +eslint-plugin-lit@^1.14.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-lit/-/eslint-plugin-lit-1.14.0.tgz#afd10b93dd6fed3dc851f9c375c3d7f6c32c59fa" + integrity sha512-J4w+CgO31621GreLFCdTUbTr5yeV2/RJ/M0myw0dykD5p9FGGIRLityQiNa6SG+JpVbmeQTQPJy4pNFmiurJ/w== + dependencies: + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + requireindex "^1.2.0" -eslint-plugin-react-refresh@^0.4.6: - version "0.4.7" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.7.tgz#1f597f9093b254f10ee0961c139a749acb19af7d" - integrity sha512-yrj+KInFmwuQS2UQcg1SF83ha1tuHC1jMQbRNyuWtlEzzKRDgAl7L4Yp4NlDUZTZNlWvHEzOtJhMi40R7JxcSw== +eslint-plugin-wc@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-wc/-/eslint-plugin-wc-2.1.1.tgz#9724db63885acbd9321ee42935827c786d9b2287" + integrity sha512-GfJo05ZgWfwAFbW6Gkf+9CMOIU6fmbd3b4nm+PKESHgUdUTmi7vawlELCrzOhdiQjXUPZxDfFIVxYt9D/v/GdQ== + dependencies: + is-valid-element-name "^1.0.0" + js-levenshtein-esm "^1.2.0" eslint-scope@^7.2.2: version "7.2.2" @@ -2028,6 +1934,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -2093,6 +2004,13 @@ is-unicode-supported@^2.0.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.0.0.tgz#fdf32df9ae98ff6ab2cedc155a5a6e895701c451" integrity sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q== +is-valid-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-valid-element-name/-/is-valid-element-name-1.0.0.tgz#26ef3fd76cdf1f122d105406e32d35b0de005981" + integrity sha512-GZITEJY2LkSjQfaIPBha7eyZv+ge0PhBR7KITeCCWvy7VBQrCUdFkvpI+HrAPQjVtVjy1LvlEkqQTHckoszruw== + dependencies: + is-potential-custom-element-name "^1.0.0" + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -2125,10 +2043,10 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -"js-tokens@^3.0.0 || ^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-levenshtein-esm@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/js-levenshtein-esm/-/js-levenshtein-esm-1.2.0.tgz#96532c34e0c90df198c9419963c64ca3cf43ae92" + integrity sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ== js-yaml@^4.1.0: version "4.1.0" @@ -2193,6 +2111,31 @@ liftoff@^4.0.0: rechoir "^0.8.0" resolve "^1.20.0" +lit-element@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-4.1.0.tgz#cea3eb25f15091e3fade07c4d917fa6aaf56ba7d" + integrity sha512-gSejRUQJuMQjV2Z59KAS/D4iElUhwKpIyJvZ9w+DIagIQjfJnhR20h2Q5ddpzXGS+fF0tMZ/xEYGMnKmaI/iww== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.2.0" + "@lit/reactive-element" "^2.0.4" + lit-html "^3.2.0" + +lit-html@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-3.2.0.tgz#cb09071a8a1f5f0850873f9143f18f0260be1fda" + integrity sha512-pwT/HwoxqI9FggTrYVarkBKFN9MlTUpLrDHubTmW4SrkL3kkqW5gxwbxMMUnbbRHBC0WTZnYHcjDSCM559VyfA== + dependencies: + "@types/trusted-types" "^2.0.2" + +lit@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/lit/-/lit-3.2.0.tgz#2189d72bccbc335f733a67bfbbd295f015e68e05" + integrity sha512-s6tI33Lf6VpDu7u4YqsSX78D28bYQulM+VAzsGch4fx2H0eLZnJsUBsPWmGYSGoKDNbjtRv02rio1o+UdPVwvw== + dependencies: + "@lit/reactive-element" "^2.0.4" + lit-element "^4.1.0" + lit-html "^3.2.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -2231,13 +2174,6 @@ log-symbols@^6.0.0: chalk "^5.3.0" is-unicode-supported "^1.3.0" -loose-envify@^1.1.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -2594,6 +2530,18 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" @@ -2720,21 +2668,6 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== -react-dom@^18.2.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.3.1.tgz#c2265d79511b57d479b3dd3fdfa51536494c5cb4" - integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.2" - -react@^18.2.0: - version "18.3.1" - resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891" - integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ== - dependencies: - loose-envify "^1.1.0" - readable-stream@^3.4.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -2766,6 +2699,11 @@ regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" +requireindex@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" + integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== + resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" @@ -2894,13 +2832,6 @@ safe-regex-test@^1.0.3: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -scheduler@^0.23.2: - version "0.23.2" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.2.tgz#414ba64a3b282892e944cf2108ecc078d115cdc3" - integrity sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ== - dependencies: - loose-envify "^1.1.0" - semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"