diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml
new file mode 100644
index 0000000..37500ac
--- /dev/null
+++ b/.github/workflows/node.js.yml
@@ -0,0 +1,31 @@
+# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
+
+name: Node.js CI
+
+on:
+  push:
+    branches: ["main"]
+  pull_request:
+    branches: ["main"]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    strategy:
+      matrix:
+        node-version: [20.x, 22.x]
+        # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
+
+    steps:
+      - uses: actions/checkout@v4
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v4
+        with:
+          node-version: ${{ matrix.node-version }}
+          cache: "npm"
+      - run: npm ci
+      - run: npx playwright install --with-deps
+      - run: npm run build --if-present
+      - run: npm test
diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml
new file mode 100644
index 0000000..9a48fed
--- /dev/null
+++ b/.github/workflows/npm-publish.yml
@@ -0,0 +1,30 @@
+name: Publish package to registries
+
+on:
+  release:
+    types: [created]
+
+  # Allows you to run this workflow manually from the Actions tab
+  workflow_dispatch:
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    permissions:
+      id-token: write
+    steps:
+      - uses: actions/checkout@v4
+      - name: Use Node.js ${{ matrix.node-version }}
+        uses: actions/setup-node@v4
+        with:
+          node-version: 21.x
+          registry-url: "https://registry.npmjs.org"
+          cache: "npm"
+      - run: npm ci
+      - run: npm run build --if-present
+      - run: npm version ${TAG_NAME} --git-tag-version=false
+        env:
+          TAG_NAME: ${{ github.event.release.tag_name }}
+      - run: npm publish --provenance --access public
+        env:
+          NODE_AUTH_TOKEN: ${{ secrets.npm_token }}
diff --git a/README.md b/README.md
index e730e0e..d14960d 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,52 @@
-# context-protocol
+# @open-wc/context-protocol
 
-## TODO
+A Lit compatible implementation of the [context-protocol community protocol](https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md).
 
-- [ ] Lint for browser support.
+## Installation
+
+```sh
+npm install --save @open-wc/context-protocol
+```
+
+## Usage
+
+A component that implements the ProviderMixin will become a _Provider_ of data and a component that implements the ConsumerMixin will become a _Consumer_ of data.
+
+```ts
+import { ProviderMixin } from "@open-wc/context-protocol";
+
+export class ProviderElement extends ProviderMixin(HTMLElement) {
+  // Set any data contexts here.
+  contexts = {
+    "number-of-unread-messages": () => {
+      return 0;
+    },
+  };
+
+  async connectedCallback() {
+    // It's also possible to provide context at any point using `updateContext`.
+
+    const response = await fetch("/api/messages/");
+    const data = await response.json();
+    this.updateContext("number-of-unread-messages", data.unreadCount);
+  }
+}
+```
+
+```ts
+import { ConsumerMixin } from "@open-wc/context-protocol";
+
+export class ConsumerElement extends ConsumerMixin(HTMLElement) {
+  contexts = {
+    // Fetch contexts that we care about and subscribe to any changes.
+    "number-of-unread-messages": (count: number) => {
+      this.textContent = `${count} unread messages!`;
+    },
+  };
+
+  connectedCallback() {
+    // It's also possible to get any context on demand without subscribing.
+    this.textContent = this.getContext("number-of-unread-messages");
+  }
+}
+```
diff --git a/consumer-element.ts b/consumer-element.ts
deleted file mode 100644
index bdf86bc..0000000
--- a/consumer-element.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { createContext, ContextEvent } from './index.js';
-
-export class ConsumerElement extends HTMLElement {
-  unsubscribe: () => void | undefined;
-
-  connectedCallback() {
-    // This component would like `hit-count` so we create that context.
-    const context = createContext('hit-count', 9001);
-
-    // We dispatch a event with that context. The event will bubble up the tree until it
-    // reaches a component that is able to provide that value to us.
-    // The event has a callback for the the value.
-    this.dispatchEvent(new ContextEvent(
-      context,
-      (count, unsubscribe) => {
-        this.textContent = `${count} hits!`;
-        this.unsubscribe = unsubscribe;
-      },
-      true
-    ));
-  }
-
-  disconnectedCallback() {
-    this.unsubscribe?.();
-    this.unsubscribe = undefined;
-  }
-}
diff --git a/context-protocol.ts b/context-protocol.ts
index 77a9cc7..69c501b 100644
--- a/context-protocol.ts
+++ b/context-protocol.ts
@@ -1,18 +1,72 @@
-type Subscriber<T> = (value: T) => void;
+// From: https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md#definitions
 
-export class ObservableMap {
-  #store = new Map<string, {value: unknown, subscribers: Set<Subscriber<unknown>>}>
+/**
+ * A context key.
+ *
+ * A context key can be any type of object, including strings and symbols. The
+ *  Context type brands the key type with the `__context__` property that
+ * carries the type of the value the context references.
+ */
+export type Context<KeyType, ValueType> = KeyType & { __context__: ValueType };
 
-  set(key: string, value: unknown, subscribers = new Set<Subscriber<unknown>>()) {
-    const data = this.#store.get(key);
-    subscribers = new Set([...subscribers, ...(data?.subscribers || new Set())]);
-    this.#store.set(key, {value, subscribers});
-    for (const subscriber of subscribers) {
-      subscriber(value);
-    }
+/**
+ * An unknown context type
+ */
+export type UnknownContext = Context<unknown, unknown>;
+
+/**
+ * A helper type which can extract a Context value type from a Context type
+ */
+export type ContextType<T extends UnknownContext> =
+  T extends Context<infer _, infer V> ? V : never;
+
+/**
+ * A function which creates a Context value object
+ */
+export const createContext = <ValueType>(key: unknown) =>
+  key as Context<typeof key, ValueType>;
+
+/**
+ * A callback which is provided by a context requester and is called with the value satisfying the request.
+ * This callback can be called multiple times by context providers as the requested value is changed.
+ */
+export type ContextCallback<ValueType> = (
+  value: ValueType,
+  unsubscribe?: () => void,
+) => void;
+
+/**
+ * An event fired by a context requester to signal it desires a named context.
+ *
+ * A provider should inspect the `context` property of the event to determine if it has a value that can
+ * satisfy the request, calling the `callback` with the requested value if so.
+ *
+ * If the requested context event contains a truthy `subscribe` value, then a provider can call the callback
+ * multiple times if the value is changed, if this is the case the provider should pass an `unsubscribe`
+ * function to the callback which requesters can invoke to indicate they no longer wish to receive these updates.
+ */
+export class ContextRequestEvent<T extends UnknownContext> extends Event {
+  public constructor(
+    public readonly context: T,
+    public readonly callback: ContextCallback<ContextType<T>>,
+    public readonly subscribe?: boolean,
+  ) {
+    super("context-request", { bubbles: true, composed: true });
   }
+}
 
-  get(key: string) {
-    return this.#store.get(key);
+/**
+ * A 'context-request' event can be emitted by any element which desires
+ * a context value to be injected by an external provider.
+ */
+declare global {
+  interface WindowEventMap {
+    "context-request": ContextRequestEvent<UnknownContext>;
+  }
+  interface ElementEventMap {
+    "context-request": ContextRequestEvent<UnknownContext>;
+  }
+  interface HTMLElementEventMap {
+    "context-request": ContextRequestEvent<UnknownContext>;
   }
 }
diff --git a/index.ts b/index.ts
index d201008..9866fc0 100644
--- a/index.ts
+++ b/index.ts
@@ -1,73 +1,141 @@
-// From: https://github.com/webcomponents-cg/community-protocols/blob/main/proposals/context.md#definitions
-
-/**
- * A Context object defines an optional initial value for a Context, as well as a name identifier for debugging purposes.
- */
-export type Context<T> = {
-  name: string;
-  initialValue?: T;
+import { ObservableMap } from "./observable-map.js";
+import {
+  createContext,
+  Context,
+  ContextRequestEvent,
+  UnknownContext,
+} from "./context-protocol.js";
+
+export interface CustomElement extends Element {
+  connectedCallback?(): void;
+  attributeChangedCallback?(
+    name: string,
+    oldValue: string | null,
+    newValue: string | null,
+  ): void;
+  disconnectedCallback?(): void;
+  adoptedCallback?(): void;
+  formAssociatedCallback?(form: HTMLFormElement): void;
+  formDisabledCallback?(disabled: boolean): void;
+  formResetCallback?(): void;
+  formStateRestoreCallback?(
+    state: unknown,
+    reason: "autocomplete" | "restore",
+  ): void;
+}
+
+export declare type Constructor<T> = new (...args: any[]) => T;
+
+type ProviderElement = CustomElement & {
+  contexts?: Record<PropertyKey, () => unknown>;
+  updateContext?(name: PropertyKey, value: unknown): void;
+};
+
+type ConsumerElement = CustomElement & {
+  contexts?: Record<PropertyKey, (data: any) => void>;
 };
 
-/**
- * An unknown context type
- */
-export type UnknownContext = Context<unknown>;
-
-/**
- * A helper type which can extract a Context value type from a Context type
- */
-export type ContextType<T extends UnknownContext> = T extends Context<infer Y>
-  ? Y
-  : never;
-
-/**
- * A function which creates a Context value object
- */
-export function createContext<T>(
-  name: string,
-  initialValue?: T
-): Readonly<Context<T>> {
-  return {
-    name,
-    initialValue,
+export function ProviderMixin<T extends Constructor<ProviderElement>>(
+  Class: T,
+): T & Constructor<ProviderElement> {
+  return class extends Class {
+    #dataStore = new ObservableMap();
+
+    connectedCallback() {
+      super.connectedCallback?.();
+
+      const contexts = "contexts" in this ? this.contexts : {};
+
+      for (const [key, value] of Object.entries(contexts || {})) {
+        this.#dataStore.set(key, value());
+      }
+
+      this.addEventListener("context-request", this.#handleContextRequest);
+    }
+
+    disconnectedCallback(): void {
+      this.#dataStore = new ObservableMap();
+      this.removeEventListener("context-request", this.#handleContextRequest);
+    }
+
+    updateContext(name: PropertyKey, value: unknown) {
+      this.#dataStore.set(createContext(name), value);
+    }
+
+    // We listen for a bubbled context request event and provide the event with the context requested.
+    #handleContextRequest(event: ContextRequestEvent<UnknownContext>) {
+      const subscribe = event.subscribe;
+      const data = this.#dataStore.get(event.context);
+      if (data) {
+        event.stopPropagation();
+
+        let unsubscribe = () => undefined;
+
+        if (subscribe) {
+          unsubscribe = () => {
+            data.subscribers.delete(event.callback);
+          };
+          data.subscribers.add(event.callback);
+        }
+
+        event.callback(data.value, unsubscribe);
+      }
+    }
   };
 }
 
-/**
- * A callback which is provided by a context requester and is called with the value satisfying the request.
- * This callback can be called multiple times by context providers as the requested value is changed.
- */
-export type ContextCallback<ValueType> = (
-  value: ValueType,
-  unsubscribe?: () => void
-) => void;
-
-/**
- * An event fired by a context requester to signal it desires a named context.
- *
- * A provider should inspect the `context` property of the event to determine if it has a value that can
- * satisfy the request, calling the `callback` with the requested value if so.
- *
- * If the requested context event contains a truthy `subscribe` value, then a provider can call the callback
- * multiple times if the value is changed, if this is the case the provider should pass an `unsubscribe`
- * function to the callback which requesters can invoke to indicate they no longer wish to receive these updates.
- */
-export class ContextEvent<T extends UnknownContext> extends Event {
-  public constructor(
-    public readonly context: T,
-    public readonly callback: ContextCallback<ContextType<T>>,
-    public readonly subscribe?: boolean
-  ) {
-    super("context-request", { bubbles: true, composed: true });
-  }
-}
+export function ConsumerMixin<T extends Constructor<ConsumerElement>>(
+  Class: T,
+): T & Constructor<ConsumerElement> {
+  return class extends Class {
+    #unsubscribes: Array<() => void> = [];
+
+    getContext(contextName: PropertyKey) {
+      let result: unknown;
 
-declare global {
-  interface HTMLElementEventMap {
-    /**
-     * A 'context-request' event can be emitted by any element which desires
-     * a context value to be injected by an external provider.
-     */
-    "context-request": ContextEvent<UnknownContext>;
-  }
+      this.dispatchEvent(
+        new ContextRequestEvent(createContext(contextName), (data) => {
+          result = data;
+        }),
+      );
+
+      return result;
+    }
+
+    connectedCallback() {
+      super.connectedCallback?.();
+
+      const contexts = "contexts" in this ? this.contexts : {};
+      for (const [contextName, callback] of Object.entries(contexts || {})) {
+        const context = createContext(contextName);
+
+        // We dispatch a event with that context. The event will bubble up the tree until it
+        // reaches a component that is able to provide that value to us.
+        // The event has a callback for the the value.
+        this.dispatchEvent(
+          new ContextRequestEvent(
+            context,
+            (data, unsubscribe) => {
+              callback(data);
+              if (unsubscribe) {
+                this.#unsubscribes.push(unsubscribe);
+              }
+            },
+            // Always subscribe. Consumers can ignore updates if they'd like.
+            true,
+          ),
+        );
+      }
+    }
+
+    // Unsubscribe from all callbacks when disconnecting
+    disconnectedCallback() {
+      for (const unsubscribe of this.#unsubscribes) {
+        unsubscribe?.();
+      }
+      // Empty out the array in case this element is still stored in memory but just not connected
+      // to the DOM.
+      this.#unsubscribes = [];
+    }
+  };
 }
diff --git a/mixins.ts b/mixins.ts
deleted file mode 100644
index 1308d90..0000000
--- a/mixins.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import { ObservableMap } from "./context-protocol.js";
-import { createContext, ContextEvent } from "./index.js";
-
-interface CustomElement extends HTMLElement {
-  new (...args: any[]): CustomElement;
-  connectedCallback?(): void;
-  attributeChangedCallback?(
-    name: string,
-    oldValue: string | null,
-    newValue: string | null,
-  ): void;
-  disconnectedCallback?(): void;
-  adoptedCallback?(): void;
-}
-
-export function ProviderMixin(Class: CustomElement) {
-  return class extends Class {
-    #dataStore = new ObservableMap();
-
-    connectedCallback() {
-      super.connectedCallback?.();
-     
-      for (const attribute in this.observedAttributes) {
-        this.#dataStore.set(attribute, this.getAttribute(attribute));
-      }
-
-      // We listen for a bubbled context request event and provide the event with the context requested.
-      this.addEventListener(
-        "context-request",
-        (event: ContextEvent<{ name: string; initialValue?: unknown }>) => {
-          const { name, initialValue } = event.context;
-          const subscribe = event.subscribe;
-          if (initialValue) {
-            this.#dataStore.set(name, initialValue);
-          }
-          const data = this.#dataStore.get(name);
-          if (data) {
-            event.stopPropagation();
-
-            let unsubscribe: () => void | undefined;
-
-            if (subscribe) {
-              unsubscribe = () => {
-                data.subscribers.delete(event.callback);
-              };
-              data.subscribers.add(event.callback);
-            }
-
-            event.callback(data.value, unsubscribe);
-          }
-        },
-      );
-    }
-
-    attributeChangedCallback(name: string, oldValue: string, newValue: string) {
-      super.attributeChangedCallback?.(name, oldValue, newValue);
-      this.#dataStore.set(name, newValue);
-    }
-
-    // TODO: Something in the disconnectedCallback??
-  };
-}
-
-export function ConsumerMixin(Class: CustomElement) {
-  return class extends Class {
-    unsubscribes: Array<() => void> = []
-
-    connectedCallback() {
-      super.connectedCallback?.();
-
-      for (const [contextName, callback] of Object.entries(this.contexts)) {
-        const context = createContext(contextName);
-
-        // We dispatch a event with that context. The event will bubble up the tree until it
-        // reaches a component that is able to provide that value to us.
-        // The event has a callback for the the value.
-        this.dispatchEvent(
-          new ContextEvent(
-            context,
-            (data, unsubscribe) => {
-              callback(data);
-              if (unsubscribe) {
-                this.unsubscribes.push(unsubscribe);
-              }
-            },
-            true,
-          ),
-        );
-      }
-    }
-
-    disconnectedCallback() {
-      for (const unsubscribe of this.unsubscribes) {
-        unsubscribe?.();
-      }
-      this.unsubscribes = [];
-    }
-  };
-}
diff --git a/observable-map.ts b/observable-map.ts
new file mode 100644
index 0000000..1b7e710
--- /dev/null
+++ b/observable-map.ts
@@ -0,0 +1,22 @@
+type Subscriber<T> = (value: T) => void;
+
+export class ObservableMap<K, V> {
+  #store = new Map<K, { value: V; subscribers: Set<Subscriber<V>> }>();
+
+  set(key: K, value: V, subscribers = new Set<Subscriber<V>>()) {
+    const data = this.#store.get(key);
+    subscribers = new Set([
+      ...subscribers,
+      ...(data?.subscribers || new Set()),
+    ]);
+
+    this.#store.set(key, { value, subscribers });
+    for (const subscriber of subscribers) {
+      subscriber(value);
+    }
+  }
+
+  get(key: K) {
+    return this.#store.get(key);
+  }
+}
diff --git a/package-lock.json b/package-lock.json
index 2b81f8c..95d5932 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,19 +1,25 @@
 {
-  "name": "context-protocol",
-  "version": "0.0.1",
+  "name": "@open-wc/context-protocol",
+  "version": "0.0.3",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
     "": {
-      "name": "context-protocol",
-      "version": "0.0.1",
+      "name": "@open-wc/context-protocol",
+      "version": "0.0.3",
       "license": "ISC",
       "devDependencies": {
+        "@open-wc/dedupe-mixin": "^1.4.0",
+        "@open-wc/scoped-elements": "^3.0.5",
         "@open-wc/testing": "^4.0.0",
+        "@types/mocha": "^10.0.6",
         "@web/dev-server-esbuild": "^1.0.2",
+        "@web/dev-server-polyfill": "^1.0.4",
         "@web/test-runner": "^0.18.0",
         "@web/test-runner-playwright": "^0.11.0",
         "chai": "^5.1.0",
+        "lit": "^3.1.2",
+        "mocha": "^10.4.0",
         "typescript": "^5.3.3"
       }
     },
@@ -39,19 +45,233 @@
         "node": ">=12.17"
       }
     },
+    "node_modules/@ampproject/remapping": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+      "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/@babel/code-frame": {
-      "version": "7.23.5",
-      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
-      "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
+      "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/highlight": "^7.24.2",
+        "picocolors": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/compat-data": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz",
+      "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/core": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz",
+      "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==",
+      "dev": true,
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.0",
+        "@babel/code-frame": "^7.24.2",
+        "@babel/generator": "^7.24.4",
+        "@babel/helper-compilation-targets": "^7.23.6",
+        "@babel/helper-module-transforms": "^7.23.3",
+        "@babel/helpers": "^7.24.4",
+        "@babel/parser": "^7.24.4",
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0",
+        "convert-source-map": "^2.0.0",
+        "debug": "^4.1.0",
+        "gensync": "^1.0.0-beta.2",
+        "json5": "^2.2.3",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/babel"
+      }
+    },
+    "node_modules/@babel/core/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/generator": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.4.tgz",
+      "integrity": "sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.24.0",
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25",
+        "jsesc": "^2.5.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets": {
+      "version": "7.23.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz",
+      "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/compat-data": "^7.23.5",
+        "@babel/helper-validator-option": "^7.23.5",
+        "browserslist": "^4.22.2",
+        "lru-cache": "^5.1.1",
+        "semver": "^6.3.1"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+      "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+      "dev": true,
+      "dependencies": {
+        "yallist": "^3.0.2"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+      "version": "6.3.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+      "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      }
+    },
+    "node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+      "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+      "dev": true
+    },
+    "node_modules/@babel/helper-environment-visitor": {
+      "version": "7.22.20",
+      "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
+      "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-function-name": {
+      "version": "7.23.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz",
+      "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.22.15",
+        "@babel/types": "^7.23.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-hoist-variables": {
+      "version": "7.22.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+      "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.22.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-imports": {
+      "version": "7.24.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz",
+      "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.24.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-module-transforms": {
+      "version": "7.23.3",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz",
+      "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-environment-visitor": "^7.22.20",
+        "@babel/helper-module-imports": "^7.22.15",
+        "@babel/helper-simple-access": "^7.22.5",
+        "@babel/helper-split-export-declaration": "^7.22.6",
+        "@babel/helper-validator-identifier": "^7.22.20"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      },
+      "peerDependencies": {
+        "@babel/core": "^7.0.0"
+      }
+    },
+    "node_modules/@babel/helper-simple-access": {
+      "version": "7.22.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+      "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/types": "^7.22.5"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-split-export-declaration": {
+      "version": "7.22.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
+      "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
       "dev": true,
       "dependencies": {
-        "@babel/highlight": "^7.23.4",
-        "chalk": "^2.4.2"
+        "@babel/types": "^7.22.5"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
+      "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/helper-validator-identifier": {
       "version": "7.22.20",
       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
@@ -61,15 +281,100 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/helper-validator-option": {
+      "version": "7.23.5",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz",
+      "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helpers": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
+      "integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/template": "^7.24.0",
+        "@babel/traverse": "^7.24.1",
+        "@babel/types": "^7.24.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/highlight": {
-      "version": "7.23.4",
-      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
-      "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
+      "version": "7.24.2",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
+      "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
       "dev": true,
       "dependencies": {
         "@babel/helper-validator-identifier": "^7.22.20",
         "chalk": "^2.4.2",
-        "js-tokens": "^4.0.0"
+        "js-tokens": "^4.0.0",
+        "picocolors": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.24.4",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
+      "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
+      "dev": true,
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@babel/template": {
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
+      "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.23.5",
+        "@babel/parser": "^7.24.0",
+        "@babel/types": "^7.24.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/traverse": {
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz",
+      "integrity": "sha512-xuU6o9m68KeqZbQuDt2TcKSxUw/mrsvavlEqQ1leZ/B+C9tk6E4sRWy97WaXgvq5E+nU3cXMxv3WKOCanVMCmQ==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.24.1",
+        "@babel/generator": "^7.24.1",
+        "@babel/helper-environment-visitor": "^7.22.20",
+        "@babel/helper-function-name": "^7.23.0",
+        "@babel/helper-hoist-variables": "^7.22.5",
+        "@babel/helper-split-export-declaration": "^7.22.6",
+        "@babel/parser": "^7.24.1",
+        "@babel/types": "^7.24.0",
+        "debug": "^4.3.1",
+        "globals": "^11.1.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/types": {
+      "version": "7.24.0",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
+      "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-string-parser": "^7.23.4",
+        "@babel/helper-validator-identifier": "^7.22.20",
+        "to-fast-properties": "^2.0.0"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -452,6 +757,20 @@
         "@types/chai": "^4.2.12"
       }
     },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+      "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.24"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
     "node_modules/@jridgewell/resolve-uri": {
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -461,6 +780,25 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/@jridgewell/set-array": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+      "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
+      "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.5",
+        "@jridgewell/trace-mapping": "^0.3.25"
+      }
+    },
     "node_modules/@jridgewell/sourcemap-codec": {
       "version": "1.4.15",
       "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
@@ -468,9 +806,9 @@
       "dev": true
     },
     "node_modules/@jridgewell/trace-mapping": {
-      "version": "0.3.23",
-      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.23.tgz",
-      "integrity": "sha512-9/4foRoUKp8s96tSkh8DlAAc5A0Ty8vLXld+l9gjKKY6ckwI8G15f0hskGmuLZu78ZlGa1vtsfOa+lnB4vG6Jg==",
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
       "dev": true,
       "dependencies": {
         "@jridgewell/resolve-uri": "^3.1.0",
@@ -1033,6 +1371,12 @@
       "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
       "dev": true
     },
+    "node_modules/@types/mocha": {
+      "version": "10.0.6",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz",
+      "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
+      "dev": true
+    },
     "node_modules/@types/node": {
       "version": "20.11.20",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz",
@@ -1233,6 +1577,19 @@
         "node": ">=18.0.0"
       }
     },
+    "node_modules/@web/dev-server-polyfill": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@web/dev-server-polyfill/-/dev-server-polyfill-1.0.4.tgz",
+      "integrity": "sha512-WV6QoRDNldtEQCV8MeaExY+XR10aobWf2E6xBq1tzpABcnoTlL49q1ywzMzpDw726C7qFPacakv4Bvmizr3R5w==",
+      "dev": true,
+      "dependencies": {
+        "@web/dev-server": "^0.4.0",
+        "@web/polyfills-loader": "^2.2.0"
+      },
+      "engines": {
+        "node": ">=16.0.0"
+      }
+    },
     "node_modules/@web/dev-server-rollup": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/@web/dev-server-rollup/-/dev-server-rollup-0.6.1.tgz",
@@ -1263,6 +1620,36 @@
         "node": ">=18.0.0"
       }
     },
+    "node_modules/@web/polyfills-loader": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@web/polyfills-loader/-/polyfills-loader-2.3.0.tgz",
+      "integrity": "sha512-71XV+CiS7jtRERM906Ia5p6Y2+B3sZp/oXN+mt3EoB7hNoIk61m5xSuz4uIsYiHwxfLBvpZK38NOb65fSmrLrw==",
+      "dev": true,
+      "dependencies": {
+        "@babel/core": "^7.12.10",
+        "@web/parse5-utils": "^2.1.0",
+        "@webcomponents/scoped-custom-element-registry": "^0.0.9",
+        "@webcomponents/shadycss": "^1.11.0",
+        "@webcomponents/webcomponentsjs": "^2.5.0",
+        "abortcontroller-polyfill": "^1.5.0",
+        "construct-style-sheets-polyfill": "^3.0.5",
+        "core-js-bundle": "^3.8.1",
+        "dynamic-import-polyfill": "^0.1.1",
+        "es-module-shims": "^1.4.1",
+        "intersection-observer": "^0.12.0",
+        "parse5": "^6.0.1",
+        "regenerator-runtime": "^0.14.0",
+        "resize-observer-polyfill": "^1.5.1",
+        "shady-css-scoped-element": "^0.0.2",
+        "systemjs": "^6.8.1",
+        "terser": "^5.14.2",
+        "urlpattern-polyfill": "^6.0.2",
+        "whatwg-fetch": "^3.5.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
     "node_modules/@web/test-runner": {
       "version": "0.18.0",
       "resolved": "https://registry.npmjs.org/@web/test-runner/-/test-runner-0.18.0.tgz",
@@ -1402,6 +1789,30 @@
         "node": ">=18.0.0"
       }
     },
+    "node_modules/@webcomponents/scoped-custom-element-registry": {
+      "version": "0.0.9",
+      "resolved": "https://registry.npmjs.org/@webcomponents/scoped-custom-element-registry/-/scoped-custom-element-registry-0.0.9.tgz",
+      "integrity": "sha512-4RVHwN9oIp23mhrxeDbP9/0wvdcrB0d/MFMqIStShIgfzsqXZXf1OdU5kvqjgdMYavQ5Zm8C6BuBcAjzNwAetQ==",
+      "dev": true
+    },
+    "node_modules/@webcomponents/shadycss": {
+      "version": "1.11.2",
+      "resolved": "https://registry.npmjs.org/@webcomponents/shadycss/-/shadycss-1.11.2.tgz",
+      "integrity": "sha512-vRq+GniJAYSBmTRnhCYPAPq6THYqovJ/gzGThWbgEZUQaBccndGTi1hdiUP15HzEco0I6t4RCtXyX0rsSmwgPw==",
+      "dev": true
+    },
+    "node_modules/@webcomponents/webcomponentsjs": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/@webcomponents/webcomponentsjs/-/webcomponentsjs-2.8.0.tgz",
+      "integrity": "sha512-loGD63sacRzOzSJgQnB9ZAhaQGkN7wl2Zuw7tsphI5Isa0irijrRo6EnJii/GgjGefIFO8AIO7UivzRhFaEk9w==",
+      "dev": true
+    },
+    "node_modules/abortcontroller-polyfill": {
+      "version": "1.7.5",
+      "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
+      "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==",
+      "dev": true
+    },
     "node_modules/accepts": {
       "version": "1.3.8",
       "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -1415,6 +1826,18 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/acorn": {
+      "version": "8.11.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/agent-base": {
       "version": "7.1.0",
       "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz",
@@ -1427,6 +1850,15 @@
         "node": ">= 14"
       }
     },
+    "node_modules/ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/ansi-escapes": {
       "version": "4.3.2",
       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
@@ -1476,6 +1908,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "node_modules/array-back": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz",
@@ -1557,6 +1995,12 @@
       "integrity": "sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==",
       "dev": true
     },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
     "node_modules/bare-events": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.2.0.tgz",
@@ -1602,6 +2046,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0"
+      }
+    },
     "node_modules/braces": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
@@ -1614,6 +2067,44 @@
         "node": ">=8"
       }
     },
+    "node_modules/browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "node_modules/browserslist": {
+      "version": "4.23.0",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz",
+      "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001587",
+        "electron-to-chromium": "^1.4.668",
+        "node-releases": "^2.0.14",
+        "update-browserslist-db": "^1.0.13"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
     "node_modules/buffer": {
       "version": "5.7.1",
       "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -1647,6 +2138,12 @@
         "node": "*"
       }
     },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
     "node_modules/builtin-modules": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
@@ -1712,6 +2209,26 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001607",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001607.tgz",
+      "integrity": "sha512-WcvhVRjXLKFB/kmOFVwELtMxyhq3iM/MvmXcyCe2PNf166c39mptscOc/45TTS96n2gpNV2z7+NakArTWZCQ3w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ]
+    },
     "node_modules/chai": {
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz",
@@ -2081,6 +2598,18 @@
         "node": ">=12.17"
       }
     },
+    "node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "node_modules/construct-style-sheets-polyfill": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-3.1.0.tgz",
+      "integrity": "sha512-HBLKP0chz8BAY6rBdzda11c3wAZeCZ+kIG4weVC2NM3AXzxx09nhe8t0SQNdloAvg5GLuHwq/0SPOOSPvtCcKw==",
+      "dev": true
+    },
     "node_modules/content-disposition": {
       "version": "0.5.4",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -2121,6 +2650,17 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/core-js-bundle": {
+      "version": "3.36.1",
+      "resolved": "https://registry.npmjs.org/core-js-bundle/-/core-js-bundle-3.36.1.tgz",
+      "integrity": "sha512-K2RZDsLbrKk4b4OdTLd8+M/+9FQ84OtMy9yYqSPM8nai8T/RqkOgaR5fiXPPNew3skeJIggwy1G/ySOg912Nig==",
+      "dev": true,
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/cross-fetch": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
@@ -2162,6 +2702,18 @@
         }
       }
     },
+    "node_modules/decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/deep-eql": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz",
@@ -2287,12 +2839,24 @@
         "node": ">=8"
       }
     },
+    "node_modules/dynamic-import-polyfill": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/dynamic-import-polyfill/-/dynamic-import-polyfill-0.1.1.tgz",
+      "integrity": "sha512-m953zv0w5oDagTItWm6Auhmk/pY7EiejaqiVbnzSS3HIjh1FCUeK7WzuaVtWPNs58A+/xpIE+/dVk6pKsrua8g==",
+      "dev": true
+    },
     "node_modules/ee-first": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
       "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
       "dev": true
     },
+    "node_modules/electron-to-chromium": {
+      "version": "1.4.729",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.729.tgz",
+      "integrity": "sha512-bx7+5Saea/qu14kmPTDHQxkp2UnziG3iajUQu3BxFvCOnpAJdDbMV4rSl+EqFDkkpNNVUFlR1kDfpL59xfy1HA==",
+      "dev": true
+    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -2350,6 +2914,12 @@
       "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==",
       "dev": true
     },
+    "node_modules/es-module-shims": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/es-module-shims/-/es-module-shims-1.9.0.tgz",
+      "integrity": "sha512-R4lwSjeELpw1Bzu2a7k3nqpxyPMfiXRq7ewqFhydV/zcYgt4b4VZzNonZu/SotJyb4ibEjuqN/OIM4wQCAGmwA==",
+      "dev": true
+    },
     "node_modules/esbuild": {
       "version": "0.19.12",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
@@ -2588,6 +3158,31 @@
         "node": ">=4.0.0"
       }
     },
+    "node_modules/find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true,
+      "bin": {
+        "flat": "cli.js"
+      }
+    },
     "node_modules/fresh": {
       "version": "0.5.2",
       "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -2611,6 +3206,12 @@
         "node": ">=14.14"
       }
     },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
     "node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -2634,6 +3235,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/gensync": {
+      "version": "1.0.0-beta.2",
+      "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -2710,6 +3320,25 @@
         "node": ">= 14"
       }
     },
+    "node_modules/glob": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+      "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^5.0.1",
+        "once": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
     "node_modules/glob-parent": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
@@ -2722,6 +3351,15 @@
         "node": ">= 6"
       }
     },
+    "node_modules/globals": {
+      "version": "11.12.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+      "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/globby": {
       "version": "11.1.0",
       "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -2832,6 +3470,15 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true,
+      "bin": {
+        "he": "bin/he"
+      }
+    },
     "node_modules/html-escaper": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -2952,12 +3599,28 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
       "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
       "dev": true
     },
+    "node_modules/intersection-observer": {
+      "version": "0.12.2",
+      "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.12.2.tgz",
+      "integrity": "sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==",
+      "dev": true
+    },
     "node_modules/ip": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz",
@@ -3091,6 +3754,15 @@
         "node": ">=0.12.0"
       }
     },
+    "node_modules/is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/is-stream": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -3103,6 +3775,18 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/is-wsl": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -3190,12 +3874,48 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/jsbn": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
       "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==",
       "dev": true
     },
+    "node_modules/jsesc": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+      "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+      "dev": true,
+      "bin": {
+        "jsesc": "bin/jsesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -3374,6 +4094,21 @@
         "@types/trusted-types": "^2.0.2"
       }
     },
+    "node_modules/locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/lodash": {
       "version": "4.17.21",
       "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -3392,6 +4127,92 @@
       "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
       "dev": true
     },
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-symbols/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/log-symbols/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/log-symbols/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/log-symbols/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/log-symbols/node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/log-update": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
@@ -3510,6 +4331,18 @@
         "node": ">=6"
       }
     },
+    "node_modules/minimatch": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+      "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/minimist": {
       "version": "1.2.8",
       "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -3543,6 +4376,207 @@
       "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==",
       "dev": true
     },
+    "node_modules/mocha": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+      "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.5.3",
+        "debug": "4.3.4",
+        "diff": "5.0.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "8.1.0",
+        "he": "1.2.0",
+        "js-yaml": "4.1.0",
+        "log-symbols": "4.1.0",
+        "minimatch": "5.0.1",
+        "ms": "2.1.3",
+        "serialize-javascript": "6.0.0",
+        "strip-json-comments": "3.1.1",
+        "supports-color": "8.1.1",
+        "workerpool": "6.2.1",
+        "yargs": "16.2.0",
+        "yargs-parser": "20.2.4",
+        "yargs-unparser": "2.0.0"
+      },
+      "bin": {
+        "_mocha": "bin/_mocha",
+        "mocha": "bin/mocha.js"
+      },
+      "engines": {
+        "node": ">= 14.0.0"
+      }
+    },
+    "node_modules/mocha/node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/mocha/node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/mocha/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/mocha/node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/mocha/node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/mocha/node_modules/diff": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/mocha/node_modules/escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/mocha/node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/mocha/node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "dev": true
+    },
+    "node_modules/mocha/node_modules/supports-color": {
+      "version": "8.1.1",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/supports-color?sponsor=1"
+      }
+    },
+    "node_modules/mocha/node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/mocha/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/mocha/node_modules/yargs-parser": {
+      "version": "20.2.4",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -3633,6 +4667,12 @@
         "webidl-conversions": "^3.0.0"
       }
     },
+    "node_modules/node-releases": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
+      "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+      "dev": true
+    },
     "node_modules/normalize-path": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -3710,6 +4750,36 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/pac-proxy-agent": {
       "version": "7.0.1",
       "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz",
@@ -3757,6 +4827,15 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/path-is-absolute": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -3796,6 +4875,12 @@
       "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
       "dev": true
     },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
     "node_modules/picomatch": {
       "version": "2.3.1",
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -4036,6 +5121,15 @@
       "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==",
       "dev": true
     },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
     "node_modules/raw-body": {
       "version": "2.5.2",
       "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
@@ -4088,6 +5182,12 @@
         "node": ">=8.10.0"
       }
     },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "dev": true
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -4097,6 +5197,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
+      "dev": true
+    },
     "node_modules/resolve": {
       "version": "1.22.8",
       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
@@ -4303,6 +5409,15 @@
         "node": ">=10"
       }
     },
+    "node_modules/serialize-javascript": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+      "dev": true,
+      "dependencies": {
+        "randombytes": "^2.1.0"
+      }
+    },
     "node_modules/set-function-length": {
       "version": "1.2.1",
       "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
@@ -4326,6 +5441,12 @@
       "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
       "dev": true
     },
+    "node_modules/shady-css-scoped-element": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/shady-css-scoped-element/-/shady-css-scoped-element-0.0.2.tgz",
+      "integrity": "sha512-Dqfl70x6JiwYDujd33ZTbtCK0t52E7+H2swdWQNSTzfsolSa6LJHnTpN4T9OpJJEq4bxuzHRLFO9RBcy/UfrMQ==",
+      "dev": true
+    },
     "node_modules/side-channel": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
@@ -4456,6 +5577,25 @@
         "node": ">= 8"
       }
     },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/source-map-support/node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/sprintf-js": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
@@ -4519,6 +5659,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/supports-color": {
       "version": "5.5.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -4543,6 +5695,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/systemjs": {
+      "version": "6.14.3",
+      "resolved": "https://registry.npmjs.org/systemjs/-/systemjs-6.14.3.tgz",
+      "integrity": "sha512-hQv45irdhXudAOr8r6SVSpJSGtogdGZUbJBRKCE5nsIS7tsxxvnIHqT4IOPWj+P+HcSzeWzHlGCGpmhPDIKe+w==",
+      "dev": true
+    },
     "node_modules/table-layout": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-3.0.2.tgz",
@@ -4604,12 +5762,39 @@
         "streamx": "^2.15.0"
       }
     },
+    "node_modules/terser": {
+      "version": "5.30.3",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz",
+      "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.3",
+        "acorn": "^8.8.2",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
       "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
       "dev": true
     },
+    "node_modules/to-fast-properties": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/to-regex-range": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -4762,6 +5947,45 @@
         "node": ">= 0.8"
       }
     },
+    "node_modules/update-browserslist-db": {
+      "version": "1.0.13",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
+      "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "update-browserslist-db": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/urlpattern-polyfill": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-6.0.2.tgz",
+      "integrity": "sha512-5vZjFlH9ofROmuWmXM9yj2wljYKgWstGwe8YTyiqM7hVum/g9LyCizPZtb3UqsuppVwety9QJmfc42VggLpTgg==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.2"
+      }
+    },
     "node_modules/v8-to-istanbul": {
       "version": "9.2.0",
       "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
@@ -4794,6 +6018,12 @@
         "node": ">=12"
       }
     },
+    "node_modules/whatwg-fetch": {
+      "version": "3.6.20",
+      "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz",
+      "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==",
+      "dev": true
+    },
     "node_modules/whatwg-url": {
       "version": "11.0.0",
       "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
@@ -4816,6 +6046,12 @@
         "node": ">=12.17"
       }
     },
+    "node_modules/workerpool": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+      "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
+      "dev": true
+    },
     "node_modules/wrap-ansi": {
       "version": "6.2.0",
       "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
@@ -4932,6 +6168,21 @@
         "node": ">=12"
       }
     },
+    "node_modules/yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^6.0.0",
+        "decamelize": "^4.0.0",
+        "flat": "^5.0.2",
+        "is-plain-obj": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/yauzl": {
       "version": "2.10.0",
       "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
@@ -4950,6 +6201,18 @@
       "engines": {
         "node": ">= 4.0.0"
       }
+    },
+    "node_modules/yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
     }
   }
 }
diff --git a/package.json b/package.json
index e7ab06c..62fcf0c 100644
--- a/package.json
+++ b/package.json
@@ -1,29 +1,40 @@
 {
-  "name": "context-protocol",
-  "version": "0.0.1",
-  "description": "",
+  "name": "@open-wc/context-protocol",
+  "version": "0.0.3",
+  "description": "A Lit compatible implementation of the context-protocol community protocol",
   "type": "module",
   "scripts": {
     "build": "tsc",
     "test": "npm run test:types && npm run test:e2e",
-    "test:e2e": "web-test-runner test/**/*.test.{js,html} --node-resolve",
+    "test:e2e": "web-test-runner",
     "test:types": "tsc --noEmit"
   },
+  "files": ["./dist/"],
+  "module": "./dist/index.js",
   "exports": {
     ".": {
       "default": "./dist/index.js",
       "types": "./dist/index.d.ts"
     }
   },
+  "repository": {
+    "url": "https://github.com/open-wc/context-protocol"
+  },
   "keywords": [],
   "author": "",
   "license": "ISC",
   "devDependencies": {
+    "@open-wc/dedupe-mixin": "^1.4.0",
+    "@open-wc/scoped-elements": "^3.0.5",
     "@open-wc/testing": "^4.0.0",
+    "@types/mocha": "^10.0.6",
     "@web/dev-server-esbuild": "^1.0.2",
+    "@web/dev-server-polyfill": "^1.0.4",
     "@web/test-runner": "^0.18.0",
     "@web/test-runner-playwright": "^0.11.0",
     "chai": "^5.1.0",
+    "lit": "^3.1.2",
+    "mocha": "^10.4.0",
     "typescript": "^5.3.3"
   }
 }
diff --git a/provider-element.ts b/provider-element.ts
deleted file mode 100644
index 39e6930..0000000
--- a/provider-element.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { ObservableMap } from "./context-protocol.js";
-import { ContextEvent } from "./index.js";
-
-export class ProviderElement extends HTMLElement {
-  #dataStore = new ObservableMap();
-  static observedAttributes = ['hit-count'];
-
-  connectedCallback() {
-    // We listen for a bubbled context request event and provide the event with the context requested.
-    this.addEventListener('context-request', (event: ContextEvent<{name: string, initialValue?: unknown}>) => {
-      const {name, initialValue} = event.context;
-      const subscribe = event.subscribe;
-      if (initialValue) {
-        this.#dataStore.set(name, initialValue);
-      }
-      const data = this.#dataStore.get(name);
-      if (data) {
-        event.stopPropagation();
-
-        let unsubscribe;
-
-        if (subscribe) {
-          unsubscribe = () => { data.subscribers.delete(event.callback) }
-          data.subscribers.add(event.callback);
-        }
-
-        event.callback(data.value, unsubscribe);
-      }
-    });
-  }
-
-  attributeChangedCallback(name: string, oldValue: string, newValue: string) {
-    this.#dataStore.set(name, newValue);
-  }
-}
diff --git a/test/mixin/mixin.test.html b/test/async.test.html
similarity index 73%
rename from test/mixin/mixin.test.html
rename to test/async.test.html
index 0e00733..5053a0f 100644
--- a/test/mixin/mixin.test.html
+++ b/test/async.test.html
@@ -2,15 +2,26 @@
 <html>
   <head>
     <script type="module">
-      import { ProviderMixin, ConsumerMixin } from '../../mixins.ts';
+      import { ProviderMixin, ConsumerMixin } from '../../index.ts';
+
+      function wait(ms) {
+        return new Promise(resolve => {
+          setTimeout(resolve, ms);
+        });
+      }
 
       window.customElements.define('server-state', class ServerState extends ProviderMixin(HTMLElement) {
-        static observedAttributes = ["hit-count"];
+        contexts = {
+          'hit-count': () => {
+            return 9001;
+          }
+        }
       });
 
       window.customElements.define('hit-count', class HitCount extends ConsumerMixin(HTMLElement) {
         contexts = {
-          'hit-count': (count) => {
+          'hit-count': async (count) => {
+            await wait(10); 
             this.textContent = `${count} hits!`;
           }
         };
@@ -18,7 +29,7 @@
     </script>
   </head>
   <body>
-    <server-state hit-count="9001">
+    <server-state>
       <hit-count>
         Loading...
       </hit-count>
@@ -36,7 +47,7 @@
           await waitUntil(() => el.textContent.trim() !== 'Loading...');
           expect(el.textContent).to.equal('9001 hits!');
 
-          provider.setAttribute('hit-count', 9002);
+          provider.updateContext('hit-count', 9002);
           await waitUntil(() => el.textContent.trim() !== '9001 hits!');
           expect(el.textContent).to.equal('9002 hits!');
         });
diff --git a/test/fixture/html/consumer-element.ts b/test/fixture/html/consumer-element.ts
new file mode 100644
index 0000000..453ef56
--- /dev/null
+++ b/test/fixture/html/consumer-element.ts
@@ -0,0 +1,9 @@
+import { ConsumerMixin } from "../../../index.js";
+
+export class ConsumerElement extends ConsumerMixin(HTMLElement) {
+  contexts = {
+    "hit-count": (count: number) => {
+      this.textContent = `${count} hits!`;
+    },
+  };
+}
diff --git a/test/fixture/html/provider-element.ts b/test/fixture/html/provider-element.ts
new file mode 100644
index 0000000..cd384a0
--- /dev/null
+++ b/test/fixture/html/provider-element.ts
@@ -0,0 +1,9 @@
+import { ProviderMixin } from "../../../index.js";
+
+export class ProviderElement extends ProviderMixin(HTMLElement) {
+  contexts = {
+    "hit-count": () => {
+      return 9001;
+    },
+  };
+}
diff --git a/test/fixture/lit/consumer-element.ts b/test/fixture/lit/consumer-element.ts
new file mode 100644
index 0000000..7dba750
--- /dev/null
+++ b/test/fixture/lit/consumer-element.ts
@@ -0,0 +1,28 @@
+import { LitElement, html } from "lit";
+import { ConsumerMixin } from "../../../index.js";
+
+export class ConsumerElement extends ConsumerMixin(LitElement) {
+  static get properties() {
+    return {
+      hitCount: { type: String },
+    };
+  }
+
+  contexts = {
+    "hit-count": (count: number) => {
+      // @ts-expect-error
+      this.hitCount = `${count} hits!`;
+    },
+  };
+
+  constructor() {
+    super();
+    // @ts-expect-error
+    this.hitCount = "Loading...";
+  }
+
+  render() {
+    // @ts-expect-error
+    return html`${this.hitCount}`;
+  }
+}
diff --git a/test/fixture/lit/provider-element.ts b/test/fixture/lit/provider-element.ts
new file mode 100644
index 0000000..8a5cbd1
--- /dev/null
+++ b/test/fixture/lit/provider-element.ts
@@ -0,0 +1,14 @@
+import { LitElement, html } from "lit";
+import { ProviderMixin } from "../../../index.js";
+
+export class ProviderElement extends ProviderMixin(LitElement) {
+  contexts = {
+    "hit-count": () => {
+      return 9001;
+    },
+  };
+
+  render() {
+    return html`<slot></slot>`;
+  }
+}
diff --git a/test/htmlelement/htmlelement.test.html b/test/htmlelement/htmlelement.test.html
deleted file mode 100644
index abf5646..0000000
--- a/test/htmlelement/htmlelement.test.html
+++ /dev/null
@@ -1,38 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <script type="module">
-      import { ProviderElement } from '../../provider-element.ts';
-      import { ConsumerElement } from '../../consumer-element.ts';
-
-      window.customElements.define('server-state', class ServerState extends ProviderElement {});
-      window.customElements.define('hit-count', class HitCount extends ConsumerElement { });
-    </script>
-  </head>
-  <body>
-    <server-state>
-      <hit-count>
-        Loading...
-      </hit-count>
-    </server-state>
-
-    <script type="module">
-      import { runTests } from "@web/test-runner-mocha";
-      import { expect } from "chai";
-      import { waitUntil } from '@open-wc/testing';
-
-      runTests(() => {
-        it("subscribes to changes", async () => {
-          const provider = document.querySelector('server-state');
-          const el = document.querySelector('hit-count');
-          await waitUntil(() => el.textContent.trim() !== 'Loading...');
-          expect(el.textContent).to.equal('9001 hits!');
-         
-          provider.setAttribute('hit-count', 9002);
-          await waitUntil(() => el.textContent.trim() !== '9001 hits!');
-          expect(el.textContent).to.equal('9002 hits!');
-        });
-      });
-    </script>
-  </body>
-</html>
diff --git a/test/mixins/parent-html-child-html/dedupe-mixin.test.ts b/test/mixins/parent-html-child-html/dedupe-mixin.test.ts
new file mode 100644
index 0000000..a13b3e5
--- /dev/null
+++ b/test/mixins/parent-html-child-html/dedupe-mixin.test.ts
@@ -0,0 +1,32 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { dedupeMixin } from "@open-wc/dedupe-mixin";
+
+import { ProviderElement } from "../../fixture/html/provider-element.js";
+import { ConsumerElement } from "../../fixture/html/consumer-element.js";
+
+const ScopedElementsMixinDeduped = dedupeMixin(ScopedElementsMixin);
+
+it("DedupeMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends ScopedElementsMixinDeduped(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends ScopedElementsMixinDeduped(ConsumerElement) {},
+  );
+
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+  expect(el?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+  expect(el?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-html-child-html/scoped-elements-mixin.test.ts b/test/mixins/parent-html-child-html/scoped-elements-mixin.test.ts
new file mode 100644
index 0000000..856bf8e
--- /dev/null
+++ b/test/mixins/parent-html-child-html/scoped-elements-mixin.test.ts
@@ -0,0 +1,27 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { ProviderElement } from "../../fixture/html/provider-element.js";
+import { ConsumerElement } from "../../fixture/html/consumer-element.js";
+
+it("ScopedElementsMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends ScopedElementsMixin(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends ScopedElementsMixin(ConsumerElement) {},
+  );
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+  expect(el?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+  expect(el?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-html-child-lit/dedupe-mixin.test.ts b/test/mixins/parent-html-child-lit/dedupe-mixin.test.ts
new file mode 100644
index 0000000..e736593
--- /dev/null
+++ b/test/mixins/parent-html-child-lit/dedupe-mixin.test.ts
@@ -0,0 +1,33 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin as HTMLScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { ScopedElementsMixin as LitScopedElementsMixin } from "@open-wc/scoped-elements/lit-element.js";
+import { dedupeMixin } from "@open-wc/dedupe-mixin";
+
+import { ProviderElement } from "../../fixture/html/provider-element.js";
+import { ConsumerElement } from "../../fixture/lit/consumer-element.js";
+
+const HTMLScopedElementsMixinDeduped = dedupeMixin(HTMLScopedElementsMixin);
+const LitScopedElementsMixinDeduped = dedupeMixin(LitScopedElementsMixin);
+
+it("DedupeMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends HTMLScopedElementsMixinDeduped(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends LitScopedElementsMixinDeduped(ConsumerElement) {},
+  );
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+  expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+  expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-html-child-lit/scoped-elements-mixin.test.ts b/test/mixins/parent-html-child-lit/scoped-elements-mixin.test.ts
new file mode 100644
index 0000000..8e3633e
--- /dev/null
+++ b/test/mixins/parent-html-child-lit/scoped-elements-mixin.test.ts
@@ -0,0 +1,66 @@
+import { LitElement, html } from "lit";
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin as HTMLScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { ScopedElementsMixin as LitScopedElementsMixin } from "@open-wc/scoped-elements/lit-element.js";
+import { ProviderMixin, ConsumerMixin } from "../../../index.js";
+
+class ProviderElement extends HTMLScopedElementsMixin(
+  ProviderMixin(HTMLElement),
+) {
+  contexts = {
+    "hit-count": () => {
+      return 9001;
+    },
+  };
+}
+
+class ConsumerElement extends LitScopedElementsMixin(
+  ConsumerMixin(LitElement),
+) {
+  static get properties() {
+    return {
+      hitCount: { type: String },
+    };
+  }
+
+  contexts = {
+    "hit-count": (count: number) => {
+      // @ts-expect-error
+      this.hitCount = `${count} hits!`;
+    },
+  };
+
+  constructor() {
+    super();
+    // @ts-expect-error
+    this.hitCount = "Loading...";
+  }
+
+  render() {
+    // @ts-expect-error
+    return html`${this.hitCount}`;
+  }
+}
+
+it("ScopedElementsMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    HTMLScopedElementsMixin(ProviderElement),
+  );
+  window.customElements.define(
+    "hit-count",
+    LitScopedElementsMixin(ConsumerElement),
+  );
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+  expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+  expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-lit-child-html/dedupe-mixin.test.ts b/test/mixins/parent-lit-child-html/dedupe-mixin.test.ts
new file mode 100644
index 0000000..a13b3e5
--- /dev/null
+++ b/test/mixins/parent-lit-child-html/dedupe-mixin.test.ts
@@ -0,0 +1,32 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { dedupeMixin } from "@open-wc/dedupe-mixin";
+
+import { ProviderElement } from "../../fixture/html/provider-element.js";
+import { ConsumerElement } from "../../fixture/html/consumer-element.js";
+
+const ScopedElementsMixinDeduped = dedupeMixin(ScopedElementsMixin);
+
+it("DedupeMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends ScopedElementsMixinDeduped(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends ScopedElementsMixinDeduped(ConsumerElement) {},
+  );
+
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+  expect(el?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+  expect(el?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-lit-child-html/scoped-elements-mixin.test.ts b/test/mixins/parent-lit-child-html/scoped-elements-mixin.test.ts
new file mode 100644
index 0000000..c4cacf7
--- /dev/null
+++ b/test/mixins/parent-lit-child-html/scoped-elements-mixin.test.ts
@@ -0,0 +1,28 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin as LitScopedElementsMixin } from "@open-wc/scoped-elements/lit-element.js";
+import { ScopedElementsMixin as HTMLScopedElementsMixin } from "@open-wc/scoped-elements/html-element.js";
+import { ProviderElement } from "../../fixture/lit/provider-element.js";
+import { ConsumerElement } from "../../fixture/html/consumer-element.js";
+
+it("ScopedElementsMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends LitScopedElementsMixin(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends HTMLScopedElementsMixin(ConsumerElement) {},
+  );
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+  expect(el?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+  expect(el?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-lit-child-lit/dedupe-mixin.test.ts b/test/mixins/parent-lit-child-lit/dedupe-mixin.test.ts
new file mode 100644
index 0000000..e9b7346
--- /dev/null
+++ b/test/mixins/parent-lit-child-lit/dedupe-mixin.test.ts
@@ -0,0 +1,32 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin } from "@open-wc/scoped-elements/lit-element.js";
+import { dedupeMixin } from "@open-wc/dedupe-mixin";
+
+import { ProviderElement } from "../../fixture/lit/provider-element.js";
+import { ConsumerElement } from "../../fixture/lit/consumer-element.js";
+
+const ScopedElementsMixinDeduped = dedupeMixin(ScopedElementsMixin);
+
+it("DedupeMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends ScopedElementsMixinDeduped(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends ScopedElementsMixinDeduped(ConsumerElement) {},
+  );
+
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+  expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+  expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/mixins/parent-lit-child-lit/scoped-elements-mixin.test.ts b/test/mixins/parent-lit-child-lit/scoped-elements-mixin.test.ts
new file mode 100644
index 0000000..ec76f5a
--- /dev/null
+++ b/test/mixins/parent-lit-child-lit/scoped-elements-mixin.test.ts
@@ -0,0 +1,27 @@
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+import { ScopedElementsMixin } from "@open-wc/scoped-elements/lit-element.js";
+import { ProviderElement } from "../../fixture/lit/provider-element.js";
+import { ConsumerElement } from "../../fixture/lit/consumer-element.js";
+
+it("ScopedElementsMixin", async () => {
+  window.customElements.define(
+    "server-state",
+    class extends ScopedElementsMixin(ProviderElement) {},
+  );
+  window.customElements.define(
+    "hit-count",
+    class extends ScopedElementsMixin(ConsumerElement) {},
+  );
+  const provider = await fixture<ProviderElement>(
+    `<server-state><hit-count>Loading...</hit-count></server-state>`,
+  );
+  const el = provider?.querySelector("hit-count");
+
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+  expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+  provider?.updateContext?.("hit-count", 9002);
+  await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+  expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+});
diff --git a/test/parent-html-child-html.test.ts b/test/parent-html-child-html.test.ts
new file mode 100644
index 0000000..03cf7f6
--- /dev/null
+++ b/test/parent-html-child-html.test.ts
@@ -0,0 +1,26 @@
+/* globals describe, it */
+
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+
+import { ProviderElement } from "./fixture/html/provider-element.js";
+import { ConsumerElement } from "./fixture/html/consumer-element.js";
+
+window.customElements.define("server-state", ProviderElement);
+window.customElements.define("hit-count", ConsumerElement);
+
+describe("Parent[HTML] => Child[HTML]", () => {
+  it("subscribes to changes", async () => {
+    const provider = await fixture<ProviderElement>(
+      `<server-state><hit-count>Loading...</hit-count></server-state>`,
+    );
+    const el = provider?.querySelector("hit-count");
+
+    await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+    expect(el?.textContent).to.equal("9001 hits!");
+
+    provider?.updateContext?.("hit-count", 9002);
+    await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+    expect(el?.textContent).to.equal("9002 hits!");
+  });
+});
diff --git a/test/parent-html-child-lit.test.ts b/test/parent-html-child-lit.test.ts
new file mode 100644
index 0000000..3f9a142
--- /dev/null
+++ b/test/parent-html-child-lit.test.ts
@@ -0,0 +1,26 @@
+/* globals describe, it */
+
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+
+import { ProviderElement } from "./fixture/html/provider-element.js";
+import { ConsumerElement } from "./fixture/lit/consumer-element.js";
+
+window.customElements.define("server-state", ProviderElement);
+window.customElements.define("hit-count", ConsumerElement);
+
+describe("Parent[HTML] => Child[Lit]", () => {
+  it("subscribes to changes", async () => {
+    const provider = await fixture<ProviderElement>(
+      `<server-state><hit-count>Loading...</hit-count></server-state>`,
+    );
+    const el = provider?.querySelector("hit-count");
+
+    await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+    expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+    provider?.updateContext?.("hit-count", 9002);
+    await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+    expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+  });
+});
diff --git a/test/parent-lit-child-html.test.ts b/test/parent-lit-child-html.test.ts
new file mode 100644
index 0000000..b5c003b
--- /dev/null
+++ b/test/parent-lit-child-html.test.ts
@@ -0,0 +1,26 @@
+/* globals describe, it */
+
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+
+import { ProviderElement } from "./fixture/lit/provider-element.js";
+import { ConsumerElement } from "./fixture/html/consumer-element.js";
+
+window.customElements.define("server-state", ProviderElement);
+window.customElements.define("hit-count", ConsumerElement);
+
+describe("Parent[Lit] => Child[HTML]", () => {
+  it("subscribes to changes", async () => {
+    const provider = await fixture<ProviderElement>(
+      `<server-state><hit-count>Loading...</hit-count></server-state>`,
+    );
+    const el = provider?.querySelector("hit-count");
+
+    await waitUntil(() => el?.textContent?.trim() !== "Loading...");
+    expect(el?.textContent).to.equal("9001 hits!");
+
+    provider?.updateContext?.("hit-count", 9002);
+    await waitUntil(() => el?.textContent?.trim() !== "9001 hits!");
+    expect(el?.textContent).to.equal("9002 hits!");
+  });
+});
diff --git a/test/parent-lit-child-lit.test.ts b/test/parent-lit-child-lit.test.ts
new file mode 100644
index 0000000..f6545bf
--- /dev/null
+++ b/test/parent-lit-child-lit.test.ts
@@ -0,0 +1,26 @@
+/* globals describe, it */
+
+import { expect } from "chai";
+import { fixture, waitUntil } from "@open-wc/testing";
+
+import { ProviderElement } from "./fixture/lit/provider-element.js";
+import { ConsumerElement } from "./fixture/lit/consumer-element.js";
+
+window.customElements.define("server-state", ProviderElement);
+window.customElements.define("hit-count", ConsumerElement);
+
+describe("Parent[Lit] => Child[Lit]", () => {
+  it("subscribes to changes", async () => {
+    const provider = await fixture<ProviderElement>(
+      `<server-state><hit-count>Loading...</hit-count></server-state>`,
+    );
+    const el = provider?.querySelector("hit-count");
+
+    await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "Loading...");
+    expect(el?.shadowRoot?.textContent).to.equal("9001 hits!");
+
+    provider?.updateContext?.("hit-count", 9002);
+    await waitUntil(() => el?.shadowRoot?.textContent?.trim() !== "9001 hits!");
+    expect(el?.shadowRoot?.textContent).to.equal("9002 hits!");
+  });
+});
diff --git a/test/simple/simple.test.html b/test/simple/simple.test.html
deleted file mode 100644
index 12a38f6..0000000
--- a/test/simple/simple.test.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <script type="module">
-      import { ContextEvent, createContext } from '../../index.ts';
-
-      window.customElements.define('server-state', class ServerState extends HTMLElement {
-        connectedCallback() {
-          // We listen for a bubbled context request event and provide the event with the context requested.
-          this.addEventListener('context-request', event => {
-            if (event.context.name === 'hit-count') {
-              event.stopPropagation();
-              event.callback(9001);
-            }
-          });
-        }
-      });
-
-      window.customElements.define('hit-count', class HitCount extends HTMLElement {
-        connectedCallback() {
-          // This component would like `hit-count` so we create that context.
-          const context = createContext('hit-count');
-
-          // We dispatch a event with that context. The event will bubble up the tree until it
-          // reaches a component that is able to provide that value to us.
-          // The event has a callback for the the value.
-          this.dispatchEvent(new ContextEvent(
-            context,
-            (count) => {
-              this.textContent = `${count} hits!`
-            }
-          ));
-        }
-      });
-    </script>
-  </head>
-  <body>
-    <server-state>
-      <hit-count>
-        Loading...
-      </hit-count>
-    </server-state>
-
-    <script type="module">
-      import { runTests } from "@web/test-runner-mocha";
-      import { expect } from "chai";
-      import { waitUntil } from '@open-wc/testing';
-
-      runTests(() => {
-        it("sums up 2 numbers", async () => {
-          const el = document.querySelector('hit-count');
-          await waitUntil(() => el.textContent.trim() !== 'Loading...');
-          expect(el.textContent).to.equal('9001 hits!');
-        });
-      });
-    </script>
-  </body>
-</html>
diff --git a/test/simple/subscribe.test.html b/test/simple/subscribe.test.html
deleted file mode 100644
index d47e414..0000000
--- a/test/simple/subscribe.test.html
+++ /dev/null
@@ -1,87 +0,0 @@
-<!doctype html>
-<html>
-  <head>
-    <script type="module">
-      import { ContextEvent, createContext } from '../../index.ts';
-      import {ObservableMap} from '../../context-protocol.ts';
-
-      window.customElements.define('server-state', class ServerState extends HTMLElement {
-        connectedCallback() {
-          this.dataStore = new ObservableMap();
-          // We listen for a bubbled context request event and provide the event with the context requested.
-          this.addEventListener('context-request', 
-            event => {
-            const {name, initialValue} = event.context;
-            const subscribe = event.subscribe;
-            if (initialValue) {
-              this.dataStore.set(name, initialValue);
-            }
-            const data = this.dataStore.get(name);
-            if (data) {
-              event.stopPropagation();
-
-              let unsubscribe;
-
-              if (subscribe) {
-                unsubscribe = () => { data.subscribes.remove(event.callback) }
-                data.subscribers.add(event.callback);
-              }
-
-              event.callback(data.value, unsubscribe);
-            }
-          });
-        }
-      });
-
-      window.customElements.define('hit-count', class HitCount extends HTMLElement {
-        connectedCallback() {
-          // This component would like `hit-count` so we create that context.
-          const context = createContext('hit-count', 9001);
-
-          // We dispatch a event with that context. The event will bubble up the tree until it
-          // reaches a component that is able to provide that value to us.
-          // The event has a callback for the the value.
-          this.dispatchEvent(new ContextEvent(
-            context,
-            (count, unsubscribe) => {
-              this.textContent = `${count} hits!`;
-              this.unsubscribe = unsubscribe;
-            },
-            true
-          ));
-        }
-
-        disconnectedCallback() {
-          this.unsubscribe?.();
-          this.unsubscribe = undefined;
-        }
-      });
-    </script>
-  </head>
-  <body>
-    <server-state>
-      <hit-count>
-        Loading...
-      </hit-count>
-    </server-state>
-
-    <script type="module">
-      import { runTests } from "@web/test-runner-mocha";
-      import { expect } from "chai";
-      import { waitUntil } from '@open-wc/testing';
-
-      runTests(() => {
-        it("subscribes to changes", async () => {
-          const provider = document.querySelector('server-state');
-          const el = document.querySelector('hit-count');
-          await waitUntil(() => el.textContent.trim() !== 'Loading...');
-          expect(el.textContent).to.equal('9001 hits!');
-         
-          provider.dataStore.set('hit-count', 9002);
-          await waitUntil(() => el.textContent.trim() !== '9001 hits!');
-          expect(el.textContent).to.equal('9002 hits!');
-        });
-      });
-    </script>
-  </body>
-</html>
diff --git a/tsconfig.json b/tsconfig.json
index 8aff000..4b282c1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -10,9 +10,11 @@
     "skipLibCheck": true,
     "forceConsistentCasingInFileNames": true,
     "lib": ["esnext", "dom"],
+    "checkJs": true,
+    "isolatedModules": true,
+    "moduleResolution": "Bundler",
+    "experimentalDecorators": true
   },
-    "files": [
-      "./provider-element.ts"
-    ],
-  "isolatedModules": true,
+  "include": ["./*.ts", "./test/*.ts"],
+  "isolatedModules": true
 }
diff --git a/web-test-runner.config.js b/web-test-runner.config.js
index b752287..1cfe888 100644
--- a/web-test-runner.config.js
+++ b/web-test-runner.config.js
@@ -1,15 +1,26 @@
-import { playwrightLauncher } from '@web/test-runner-playwright';
-import { esbuildPlugin } from '@web/dev-server-esbuild';
+import { polyfill } from "@web/dev-server-polyfill";
+import { playwrightLauncher } from "@web/test-runner-playwright";
+import { esbuildPlugin } from "@web/dev-server-esbuild";
 
 export default {
+  files: ["test/**/*.test.{js,ts}"],
   nodeResolve: true,
-  plugins: [esbuildPlugin({ ts: true })],
-  browsers: [
-    playwrightLauncher({
-      launchOptions: {
-        headless: false,
-        devtools: true
-      },
+  plugins: [
+    esbuildPlugin({ ts: true }),
+    polyfill({
+      scopedCustomElementRegistry: true,
     }),
   ],
-}
+  filterBrowserLogs(log) {
+    return (
+      log.args[0] !==
+      "Lit is in dev mode. Not recommended for production! See https://lit.dev/msg/dev-mode for more information."
+    );
+  },
+  browsers: [
+    playwrightLauncher({ product: "chromium" }),
+    playwrightLauncher({ product: "firefox" }),
+    playwrightLauncher({ product: "webkit" }),
+  ],
+  fullyParallel: true,
+};
