diff --git a/.github/workflows/web-docker.yml b/.github/workflows/web-docker.yml new file mode 100644 index 00000000000..151f07bb490 --- /dev/null +++ b/.github/workflows/web-docker.yml @@ -0,0 +1,91 @@ +name: Web client CI + +on: + pull_request: + paths: + - 'flutter/web/**' + - 'flutter/lib/**' + - 'flutter/assets/**' + - 'flutter/pubspec.*' + - 'Dockerfile.webclient' + - '.github/workflows/web-docker.yml' + - '.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff' + push: + tags: ['*'] + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository_owner }}/rustdesk-webclient + +jobs: + test: + runs-on: ubuntu-22.04 + permissions: + contents: read + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + persist-credentials: false + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install protoc + run: sudo apt-get update && sudo apt-get install -y protobuf-compiler + + - name: Install and test + working-directory: flutter/web/js + run: | + yarn install + python3 ./gen_js_from_hbb.py > src/gen_js_from_hbb.ts + python3 ./ts_proto.py + yarn test + + build: + needs: test + runs-on: ubuntu-22.04 + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + persist-credentials: false + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: startsWith(github.ref, 'refs/tags/') + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: Dockerfile.webclient + push: ${{ startsWith(github.ref, 'refs/tags/') }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 28e089a8a72..44ee114a8da 100644 --- a/.gitignore +++ b/.gitignore @@ -61,3 +61,9 @@ rustdesk-data/ # web client build artifacts flutter/web/ogvjs*/ flutter/web/web_deps.tar.gz +flutter/web/js/coverage/ +# core dumps +core +flutter/core +# playwright testing artifacts +.playwright-mcp/ diff --git a/Dockerfile.web b/Dockerfile.web index 9e57fee47e6..b9c2ccd0583 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -5,6 +5,11 @@ ARG NODE_MAJOR=22 RUN apt-get update && apt-get install -y --no-install-recommends \ wget curl git unzip xz-utils ca-certificates python3 gnupg \ + && curl -fsSL https://dl.google.com/linux/linux_signing_key.pub \ + | gpg --dearmor -o /usr/share/keyrings/google-chrome.gpg \ + && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/google-chrome.gpg] http://dl.google.com/linux/chrome/deb/ stable main" \ + > /etc/apt/sources.list.d/google-chrome.list \ + && apt-get update && apt-get install -y --no-install-recommends google-chrome-stable \ && rm -rf /var/lib/apt/lists/* RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ @@ -22,6 +27,12 @@ ENV PATH="/opt/flutter/bin:${PATH}" RUN npm install -g yarn typescript protoc +RUN apt-get update && apt-get install -y --no-install-recommends build-essential pkg-config libclang-dev \ + && rm -rf /var/lib/apt/lists/* +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable +ENV PATH="/root/.cargo/bin:${PATH}" +RUN cargo install flutter_rust_bridge_codegen --version 1.80.1 --features uuid --locked + COPY .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff /tmp/flutter.diff RUN cd /opt/flutter && git apply /tmp/flutter.diff && rm /tmp/flutter.diff @@ -29,13 +40,18 @@ ARG UID=1000 ARG GID=1000 RUN (groupadd -g ${GID} builder 2>/dev/null || groupadd builder) \ && (useradd -m -u ${UID} -g builder builder 2>/dev/null || useradd -m -g builder builder) \ - && chown -R builder:builder /opt/flutter + && chown -R builder:builder /opt/flutter \ + && cp -r /root/.cargo /home/builder/.cargo \ + && cp -r /root/.rustup /home/builder/.rustup \ + && chown -R builder:builder /home/builder/.cargo /home/builder/.rustup USER builder +ENV PATH="/home/builder/.cargo/bin:${PATH}" +ENV RUSTUP_HOME="/home/builder/.rustup" RUN flutter config --no-analytics && flutter precache --web RUN git config --global --add safe.directory /opt/flutter \ && git config --global --add safe.directory /app -WORKDIR /app/flutter +WORKDIR /app diff --git a/Dockerfile.webclient b/Dockerfile.webclient new file mode 100644 index 00000000000..b055662e316 --- /dev/null +++ b/Dockerfile.webclient @@ -0,0 +1,68 @@ +FROM --platform=linux/amd64 ubuntu:22.04 AS build + +ARG FLUTTER_VERSION=3.24.5 +ARG NODE_MAJOR=22 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + wget curl git unzip xz-utils ca-certificates python3 gnupg \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ + | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg \ + && echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" \ + > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update && apt-get install -y nodejs \ + && rm -rf /var/lib/apt/lists/* + +RUN wget -q https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz \ + && tar xf flutter_linux_${FLUTTER_VERSION}-stable.tar.xz -C /opt \ + && rm flutter_linux_${FLUTTER_VERSION}-stable.tar.xz + +ENV PATH="/opt/flutter/bin:${PATH}" + +RUN npm install -g yarn typescript protoc + +COPY .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff /tmp/flutter.diff +RUN cd /opt/flutter && git apply /tmp/flutter.diff && rm /tmp/flutter.diff + +RUN flutter config --no-analytics && flutter precache --web + +RUN git config --global --add safe.directory /opt/flutter \ + && git config --global --add safe.directory /build + +WORKDIR /build +COPY . . + +WORKDIR /build/flutter/web/js +RUN yarn install && yarn build + +WORKDIR /build/flutter +ARG WEB_DEPS_SHA256=b66011c4fc066b90c46ba0c78884fe5d1a7e5a7fad3dce401300ad893de63818 +RUN wget -q -O /tmp/web_deps.tar.gz \ + https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/web_deps.tar.gz \ + && echo "${WEB_DEPS_SHA256} /tmp/web_deps.tar.gz" | sha256sum -c - \ + && flutter pub get && flutter build web --release \ + && tar xzf /tmp/web_deps.tar.gz -C build/web + +# --- Production stage --- +FROM nginx:alpine + +COPY --from=build /build/flutter/build/web /usr/share/nginx/html + +COPY <<'EOF' /docker-entrypoint.d/90-rustdesk-config.sh +#!/bin/sh +set -e +CONFIG=/usr/share/nginx/html/config.json +if [ -n "$RUSTDESK_HOST" ]; then + cat > "$CONFIG" < src/gen_js_from_hbb.ts && python3 ./ts_proto.py && tsc && vite build", - "preview": "vite preview" + "preview": "vite preview", + "test": "vitest run" }, "devDependencies": { + "@vitest/coverage-v8": "^4.1.11", + "jsdom": "^30.0.1", "typescript": "^4.4.4", - "vite": "2.8" + "vite": "2.8", + "vitest": "^4.1.11" }, "dependencies": { "@types/node": "^18.19.130", diff --git a/flutter/web/js/src/codec.test.ts b/flutter/web/js/src/codec.test.ts new file mode 100644 index 00000000000..8d8604846e8 --- /dev/null +++ b/flutter/web/js/src/codec.test.ts @@ -0,0 +1,57 @@ +/** + * @vitest-environment jsdom + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("wasm-feature-detect", () => ({ + simd: vi.fn(), +})); + +import { simd } from "wasm-feature-detect"; +import { loadVp9 } from "./codec"; + +describe("loadVp9", () => { + let mockDecoder: any; + let mockLoadClass: any; + + beforeEach(() => { + mockDecoder = { + init: vi.fn((cb: Function) => cb(mockDecoder)), + }; + mockLoadClass = vi.fn((className: string, callback: Function, opts: any) => { + callback(vi.fn().mockResolvedValue(mockDecoder)); + }); + (window as any).OGVLoader = { loadClass: mockLoadClass }; + (window as any).videoCodecClass = undefined; + }); + + it("loads SIMD codec when SIMD is supported", async () => { + vi.mocked(simd).mockResolvedValue(true); + const callback = vi.fn(); + await loadVp9(callback); + expect(mockLoadClass).toHaveBeenCalledWith( + "OGVDecoderVideoVP9SIMDW", + expect.any(Function), + { worker: true, threading: true } + ); + expect(callback).toHaveBeenCalledWith(mockDecoder); + }); + + it("loads non-SIMD codec when SIMD is not supported", async () => { + vi.mocked(simd).mockResolvedValue(false); + const callback = vi.fn(); + await loadVp9(callback); + expect(mockLoadClass).toHaveBeenCalledWith( + "OGVDecoderVideoVP9W", + expect.any(Function), + { worker: true, threading: true } + ); + expect(callback).toHaveBeenCalledWith(mockDecoder); + }); + + it("sets window.videoCodecClass", async () => { + vi.mocked(simd).mockResolvedValue(false); + await loadVp9(vi.fn()); + expect((window as any).videoCodecClass).toBeDefined(); + }); +}); diff --git a/flutter/web/js/src/common.test.ts b/flutter/web/js/src/common.test.ts new file mode 100644 index 00000000000..0b6af39922d --- /dev/null +++ b/flutter/web/js/src/common.test.ts @@ -0,0 +1,120 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("zstddec", () => { + const mockDecode = vi.fn((data: Uint8Array, size: number) => new Uint8Array(10)); + class MockZSTDDecoder { + init = vi.fn().mockResolvedValue(undefined); + decode = mockDecode; + } + return { ZSTDDecoder: MockZSTDDecoder }; +}); + +vi.mock("./message", () => ({ + KeyEvent: { fromPartial: (v: any) => v }, + controlKeyFromJSON: (name: string) => { + const known: Record = { Return: 1, Escape: 2, Tab: 3 }; + return known[name] ?? -1; + }, + ControlKey: { UNRECOGNIZED: -1 }, +})); + +vi.mock("./gen_js_from_hbb", () => ({ + KEY_MAP: { Enter: "Return", Esc: "Escape" } as Record, + LANGS: { + en: { Hello: "Hello", Goodbye: "Goodbye" }, + zh: { Hello: "你好" }, + }, +})); + +import { decompress, mapKey, sleep, translate, initZstd } from "./common"; + +describe("sleep", () => { + it("resolves after the given delay", async () => { + const start = Date.now(); + await sleep(50); + expect(Date.now() - start).toBeGreaterThanOrEqual(40); + }); +}); + +describe("translate", () => { + it("returns translation for known language", () => { + expect(translate("xx_zh", "Hello")).toBe("你好"); + }); + + it("falls back to English for missing key", () => { + expect(translate("xx_zh", "Goodbye")).toBe("Goodbye"); + }); + + it("returns original text when not in any dictionary", () => { + expect(translate("xx_en", "Unknown")).toBe("Unknown"); + }); + + it("falls back to English for unknown language", () => { + expect(translate("xx_fr", "Hello")).toBe("Hello"); + }); +}); + +describe("mapKey", () => { + it("maps known key names via KEY_MAP", () => { + const result = mapKey("Enter", true); + expect(result).toEqual({ control_key: 1 }); + }); + + it("maps Esc via KEY_MAP", () => { + const result = mapKey("Esc", true); + expect(result).toEqual({ control_key: 2 }); + }); + + it("maps single character to chr on desktop", () => { + const result = mapKey("a", true); + expect(result).toEqual({ chr: 97 }); + }); + + it("maps non-alpha single char to unicode on mobile", () => { + const result = mapKey("1", false); + expect(result).toEqual({ unicode: 49 }); + }); + + it("maps alpha char to chr on mobile", () => { + const result = mapKey("a", false); + expect(result).toEqual({ chr: 97 }); + }); + + it("logs error for unknown control key", () => { + const spy = vi.spyOn(console, "error").mockImplementation(() => {}); + mapKey("UnknownKey", true); + expect(spy).toHaveBeenCalledWith(expect.stringContaining("Unknown control key")); + spy.mockRestore(); + }); +}); + +describe("decompress", () => { + it("decompresses data using zstd", async () => { + const input = new Uint8Array([1, 2, 3]); + const result = await decompress(input); + expect(result).toBeInstanceOf(Uint8Array); + }); + + it("initializes zstd on first call", async () => { + const result = await decompress(new Uint8Array([1])); + expect(result).toBeDefined(); + }); + + it("clamps buffer size to MAX", async () => { + const large = new Uint8Array(10 * 1024 * 1024); + const result = await decompress(large); + expect(result).toBeDefined(); + }); + + it("uses MIN buffer size for small input", async () => { + const small = new Uint8Array([1]); + const result = await decompress(small); + expect(result).toBeDefined(); + }); +}); + +describe("initZstd", () => { + it("initializes without error", async () => { + await expect(initZstd()).resolves.toBeUndefined(); + }); +}); diff --git a/flutter/web/js/src/connection.test.ts b/flutter/web/js/src/connection.test.ts new file mode 100644 index 00000000000..abb35b95bba --- /dev/null +++ b/flutter/web/js/src/connection.test.ts @@ -0,0 +1,791 @@ +/** + * @vitest-environment jsdom + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("./message.js", () => ({ + Message: { + encode: vi.fn(() => ({ finish: () => new Uint8Array([1]) })), + fromPartial: vi.fn((v: any) => v), + decode: vi.fn(() => ({})), + }, + Hash: { fromPartial: vi.fn((v: any) => v) }, + LoginRequest: { fromPartial: vi.fn((v: any) => v) }, + OptionMessage: { fromPartial: vi.fn((v: any) => v) }, + OptionMessage_BoolOption: { Yes: 1, No: 0 }, + KeyEvent: { fromPartial: vi.fn((v: any) => v) }, + MouseEvent: { fromPartial: vi.fn((v: any) => v) }, + Misc: { fromPartial: vi.fn((v: any) => v) }, + SwitchDisplay: { fromPartial: vi.fn((v: any) => v) }, + PublicKey: { fromPartial: vi.fn((v: any) => v) }, + PeerInfo: { fromPartial: vi.fn((v: any) => v) }, + IdPk: { decode: vi.fn(() => ({ id: "test", pk: new Uint8Array(32) })) }, + ControlKey: { + Alt: 1, Control: 2, Shift: 3, Meta: 4, + CtrlAltDel: 100, Delete: 101, LockScreen: 102, + }, + ImageQuality: { Low: 0, Best: 1, Balanced: 2 }, + PermissionInfo_Permission: { Keyboard: 1, Clipboard: 2, Audio: 3 }, +})); + +vi.mock("./rendezvous.js", () => ({ + RendezvousMessage: { + encode: vi.fn(() => ({ finish: () => new Uint8Array([1]) })), + fromPartial: vi.fn((v: any) => v), + decode: vi.fn(() => ({})), + }, + ConnType: { DEFAULT_CONN: 0 }, + NatType: { SYMMETRIC: 0 }, + PunchHoleRequest: { fromPartial: vi.fn((v: any) => v) }, + PunchHoleResponse_Failure: { + UNRECOGNIZED: -1, ID_NOT_EXIST: 0, OFFLINE: 1, LICENSE_MISMATCH: 2, LICENSE_OVERUSE: 3, + }, + RequestRelay: { fromPartial: vi.fn((v: any) => v) }, + RelayResponse: { fromPartial: vi.fn((v: any) => v) }, +})); + +let nextWsResponse: any = {}; +let nextWsResponses: any[] = []; + +vi.mock("./websock", () => { + class MockWebsock { + sendMessage = vi.fn(); + sendRendezvous = vi.fn(); + open = vi.fn().mockResolvedValue(undefined); + next = vi.fn().mockImplementation(() => { + if (nextWsResponses.length) return Promise.resolve(nextWsResponses.shift()); + return Promise.resolve(nextWsResponse); + }); + close = vi.fn(); + setSecretKey = vi.fn(); + } + return { default: MockWebsock }; +}); + +vi.mock("./codec", () => ({ + loadVp9: vi.fn((cb: Function) => { + cb({ processFrame: vi.fn(), close: vi.fn() }); + }), +})); + +vi.mock("fast-sha256", () => { + class MockHash { + update = vi.fn(); + digest = vi.fn(() => new Uint8Array(32)); + } + return { Hash: MockHash }; +}); + +vi.mock("./globals", () => ({ + msgbox: vi.fn(), + draw: vi.fn(), + pushEvent: vi.fn(), + getPeers: vi.fn(() => ({})), + isDesktop: vi.fn(() => true), + verify: vi.fn().mockResolvedValue(new Uint8Array(32)), + genBoxKeyPair: vi.fn(() => [new Uint8Array(32), new Uint8Array(32)]), + genSecretKey: vi.fn(() => new Uint8Array(32)), + seal: vi.fn(() => new Uint8Array(48)), + copyToClipboard: vi.fn(), + initAudio: vi.fn(), + playAudio: vi.fn(), +})); + +vi.mock("./common", () => ({ + decompress: vi.fn().mockResolvedValue(new Uint8Array(10)), + mapKey: vi.fn((name: string) => ({ chr: name.charCodeAt(0) })), + sleep: vi.fn().mockResolvedValue(undefined), +})); + +vi.mock("./url", () => ({ + loadConfig: vi.fn().mockResolvedValue(undefined), + getConfigKey: vi.fn(() => "testkey"), + getDefaultUri: vi.fn((relay?: boolean) => relay ? "ws://relay:21119" : "ws://host:21118"), + getHost: vi.fn(() => "host:21116"), + getRelayHost: vi.fn(() => "relay:21117"), +})); + +import Connection, { getConfigHost, getConfigRelay } from "./connection"; +import * as globals from "./globals"; +import { IdPk } from "./message.js"; + +describe("getConfigHost / getConfigRelay", () => { + it("returns host from url module", () => { + expect(getConfigHost()).toBe("host:21116"); + }); + + it("returns relay from url module", () => { + expect(getConfigRelay()).toBe("relay:21117"); + }); +}); + +describe("Connection", () => { + let conn: Connection; + + let mockWs: any; + + beforeEach(() => { + localStorage.clear(); + vi.clearAllMocks(); + nextWsResponse = {}; + nextWsResponses = []; + conn = new Connection(); + (conn as any)._options = {}; + (conn as any)._id = "test-id"; + mockWs = { + sendMessage: vi.fn(), + sendRendezvous: vi.fn(), + open: vi.fn().mockResolvedValue(undefined), + next: vi.fn().mockResolvedValue({}), + close: vi.fn(), + setSecretKey: vi.fn(), + }; + (conn as any)._ws = mockWs; + }); + + describe("constructor", () => { + it("initializes with empty state", () => { + const c = new Connection(); + expect((c as any)._msgs).toEqual([]); + expect((c as any)._id).toBe(""); + expect((c as any)._videoTestSpeed).toEqual([0, 0]); + }); + }); + + describe("getOption / setOption", () => { + it("gets and sets options", () => { + conn.setOption("test", "value"); + expect(conn.getOption("test")).toBe("value"); + }); + + it("deletes option when value is undefined", () => { + conn.setOption("test", "value"); + conn.setOption("test", undefined); + expect(conn.getOption("test")).toBeUndefined(); + }); + + it("persists to localStorage", () => { + conn.setOption("foo", "bar"); + const peers = JSON.parse(localStorage.getItem("peers")!); + expect(peers["test-id"].foo).toBe("bar"); + }); + }); + + describe("getRemember / setRemember", () => { + it("returns false by default", () => { + expect(conn.getRemember()).toBe(false); + }); + + it("returns true when set", () => { + conn.setRemember(true); + expect(conn.getRemember()).toBe(true); + }); + }); + + describe("getMod", () => { + it("returns empty array when no modifiers", () => { + expect(conn.getMod(false, false, false, false)).toEqual([]); + }); + + it("returns all modifiers when all true", () => { + const mods = conn.getMod(true, true, true, true); + expect(mods).toHaveLength(4); + }); + + it("returns only alt when alt is true", () => { + expect(conn.getMod(true, false, false, false)).toEqual([1]); + }); + }); + + describe("getImageQuality", () => { + it("returns image-quality option", () => { + conn.setOption("image-quality", "best"); + expect(conn.getImageQuality()).toBe("best"); + }); + }); + + describe("getImageQualityEnum", () => { + it("returns Low for 'low'", () => { + expect(conn.getImageQualityEnum("low", false)).toBe(0); + }); + + it("returns Best for 'best'", () => { + expect(conn.getImageQualityEnum("best", false)).toBe(1); + }); + + it("returns Balanced for 'balanced' when not ignoring default", () => { + expect(conn.getImageQualityEnum("balanced", false)).toBe(2); + }); + + it("returns undefined for 'balanced' when ignoring default", () => { + expect(conn.getImageQualityEnum("balanced", true)).toBeUndefined(); + }); + + it("returns undefined for unknown quality", () => { + expect(conn.getImageQualityEnum("unknown", false)).toBeUndefined(); + }); + }); + + describe("getOptionMessage", () => { + it("returns undefined when no options set", () => { + expect(conn.getOptionMessage()).toBeUndefined(); + }); + + it("returns message with show-remote-cursor", () => { + (conn as any)._options["show-remote-cursor"] = true; + const msg = conn.getOptionMessage(); + expect(msg).toBeDefined(); + expect(msg!.show_remote_cursor).toBe(1); + }); + + it("returns message with image quality", () => { + conn.setOption("image-quality", "best"); + const msg = conn.getOptionMessage(); + expect(msg).toBeDefined(); + }); + + it("returns message with multiple options", () => { + (conn as any)._options["disable-audio"] = true; + (conn as any)._options["disable-clipboard"] = true; + (conn as any)._options["lock-after-session-end"] = true; + (conn as any)._options["privacy-mode"] = true; + const msg = conn.getOptionMessage(); + expect(msg).toBeDefined(); + }); + }); + + describe("shouldAutoLogin", () => { + it("returns empty string by default", () => { + expect(conn.shouldAutoLogin()).toBe(""); + }); + + it("returns password when all conditions met", () => { + conn.setOption("os-password", "secret"); + conn.setOption("lock-after-session-end", true); + conn.setOption("auto-login", true); + expect(conn.shouldAutoLogin()).toBe("secret"); + }); + }); + + describe("close", () => { + it("clears msgs and closes ws", () => { + (conn as any)._msgs = [{ test: true }]; + conn.close(); + expect((conn as any)._msgs).toEqual([]); + expect(mockWs.close).toHaveBeenCalled(); + }); + }); + + describe("refresh", () => { + it("sends refresh_video message", () => { + conn.refresh(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("inputKey", () => { + it("sends key event", () => { + conn.inputKey("a", true, false, false, false, false, false); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("clears alt modifier for VK_MENU", () => { + conn.inputKey("VK_MENU", true, false, true, false, false, false); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.modifiers).toEqual([]); + }); + + it("clears ctrl modifier for VK_CONTROL", () => { + conn.inputKey("VK_CONTROL", true, false, false, true, false, false); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.modifiers).toEqual([]); + }); + + it("clears shift modifier for VK_SHIFT", () => { + conn.inputKey("VK_SHIFT", true, false, false, false, true, false); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.modifiers).toEqual([]); + }); + + it("clears command modifier for Meta", () => { + conn.inputKey("Meta", true, false, false, false, false, true); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.modifiers).toEqual([]); + }); + }); + + describe("inputString", () => { + it("sends key event with seq", () => { + conn.inputString("hello"); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("inputMouse", () => { + it("sends mouse event", () => { + conn.inputMouse(1, 100, 200, false, false, false, false); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("sends mouse event with default params", () => { + conn.inputMouse(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("ctrlAltDel", () => { + it("sends CtrlAltDel for Windows", () => { + (conn as any)._peerInfo = { platform: "Windows" }; + conn.ctrlAltDel(); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.control_key).toBe(100); + }); + + it("sends Delete+mods for non-Windows", () => { + (conn as any)._peerInfo = { platform: "Linux" }; + conn.ctrlAltDel(); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.control_key).toBe(101); + }); + }); + + describe("lockScreen", () => { + it("sends LockScreen key event", () => { + conn.lockScreen(); + const call = mockWs.sendMessage.mock.calls[0][0]; + expect(call.key_event.control_key).toBe(102); + }); + }); + + describe("switchDisplay", () => { + it("sends switch_display misc message", () => { + conn.switchDisplay(1); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("setImageQuality", () => { + it("sends image quality option", () => { + conn.setImageQuality("best"); + expect(conn.getOption("image-quality")).toBe("best"); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("does not send when quality is undefined", () => { + conn.setImageQuality("unknown"); + expect(mockWs.sendMessage).not.toHaveBeenCalled(); + }); + }); + + describe("toggleOption", () => { + it("toggles show-remote-cursor", () => { + conn.toggleOption("show-remote-cursor"); + expect(conn.getOption("show-remote-cursor")).toBe(true); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("toggles disable-audio", () => { + conn.toggleOption("disable-audio"); + expect(conn.getOption("disable-audio")).toBe(true); + }); + + it("toggles disable-clipboard", () => { + conn.toggleOption("disable-clipboard"); + expect(conn.getOption("disable-clipboard")).toBe(true); + }); + + it("toggles lock-after-session-end", () => { + conn.toggleOption("lock-after-session-end"); + expect(conn.getOption("lock-after-session-end")).toBe(true); + }); + + it("toggles privacy-mode", () => { + conn.toggleOption("privacy-mode"); + expect(conn.getOption("privacy-mode")).toBe(true); + }); + + it("handles block-input without setting option", () => { + conn.toggleOption("block-input"); + expect(conn.getOption("block-input")).toBeUndefined(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("handles unblock-input", () => { + conn.toggleOption("unblock-input"); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("ignores unknown option", () => { + conn.toggleOption("unknown-option"); + expect(mockWs.sendMessage).not.toHaveBeenCalled(); + }); + }); + + describe("login", () => { + beforeEach(() => { + (conn as any)._hash = { + salt: new Uint8Array(16), + challenge: new Uint8Array(16), + }; + }); + + it("sends login with hashed password", () => { + conn.login("mypassword"); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("sends login with stored password", () => { + (conn as any)._password = new Uint8Array(32); + conn.login(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("sends login without password", () => { + conn.login(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("handlePeerInfo", () => { + it("shows error when no displays", () => { + conn.handlePeerInfo({ displays: [] } as any); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Remote Error", "No Display"); + }); + + it("pushes peer_info event on success", () => { + + conn.handlePeerInfo({ + displays: [{ x: 0, y: 0, width: 1920, height: 1080 }], + username: "user", + } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("peer_info", expect.anything()); + }); + + it("saves password when remember is true", () => { + conn.setRemember(true); + (conn as any)._password = new Uint8Array([1, 2, 3]); + conn.handlePeerInfo({ + displays: [{ x: 0, y: 0, width: 1920, height: 1080 }], + } as any); + expect(conn.getOption("password")).toBeDefined(); + }); + + it("clears password when remember is false", () => { + conn.setOption("password", "old"); + conn.handlePeerInfo({ + displays: [{ x: 0, y: 0, width: 1920, height: 1080 }], + } as any); + expect(conn.getOption("password")).toBeUndefined(); + }); + }); + + describe("handleMisc", () => { + it("handles audio_format", () => { + + conn.handleMisc({ audio_format: { channels: 2, sample_rate: 48000 } } as any); + expect(globals.initAudio).toHaveBeenCalledWith(2, 48000); + }); + + it("handles chat_message", () => { + + conn.handleMisc({ chat_message: { text: "hello" } } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("chat", { text: "hello" }); + }); + + it("handles permission_info for keyboard", () => { + + conn.handleMisc({ permission_info: { permission: 1, enabled: true } } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("permission", { keyboard: true }); + }); + + it("handles permission_info for clipboard", () => { + + conn.handleMisc({ permission_info: { permission: 2, enabled: true } } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("permission", { clipboard: true }); + }); + + it("handles permission_info for audio", () => { + + conn.handleMisc({ permission_info: { permission: 3, enabled: false } } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("permission", { audio: false }); + }); + + it("returns undefined for unknown permission", () => { + const result = conn.handleMisc({ permission_info: { permission: 99, enabled: true } } as any); + expect(result).toBeUndefined(); + }); + + it("handles switch_display", () => { + + conn.handleMisc({ switch_display: { display: 1 } } as any); + expect(globals.pushEvent).toHaveBeenCalledWith("switch_display", { display: 1 }); + }); + + it("handles close_reason and returns false", () => { + const result = conn.handleMisc({ close_reason: "Kicked" } as any); + expect(result).toBe(false); + }); + + it("returns true for other misc messages", () => { + const result = conn.handleMisc({ audio_format: { channels: 1, sample_rate: 44100 } } as any); + expect(result).toBe(true); + }); + }); + + describe("msgbox", () => { + it("calls the msgbox callback", () => { + + conn.msgbox("info", "Title", "Text"); + expect(globals.msgbox).toHaveBeenCalledWith("info", "Title", "Text"); + }); + }); + + describe("draw", () => { + it("calls draw callback and globals.draw", () => { + + const frame = { y: new Uint8Array(1) }; + conn.draw(frame); + expect(globals.draw).toHaveBeenCalledWith(frame); + }); + }); + + describe("setMsgbox / setDraw", () => { + it("sets custom msgbox callback", () => { + const custom = vi.fn(); + conn.setMsgbox(custom); + conn.msgbox("info", "T", "M"); + expect(custom).toHaveBeenCalledWith("info", "T", "M"); + }); + + it("sets custom draw callback", () => { + const custom = vi.fn(); + conn.setDraw(custom); + conn.draw({ test: true }); + expect(custom).toHaveBeenCalledWith({ test: true }); + }); + }); + + describe("sendVideoReceived", () => { + it("sends video_received misc message", () => { + conn.sendVideoReceived(); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("start", () => { + it("catches and displays connection errors", async () => { + + (conn as any)._start = vi.fn().mockRejectedValue(new Error("fail")); + await conn.start("test"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Connection Error", "Error: fail"); + }); + + it("shows 'Reset by the peer' for close events", async () => { + + (conn as any)._start = vi.fn().mockRejectedValue({ type: "close" }); + await conn.start("test"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Connection Error", "Reset by the peer"); + }); + }); + + describe("reconnect", () => { + it("closes and restarts connection", async () => { + const closeSpy = vi.spyOn(conn, "close"); + const startSpy = vi.spyOn(conn, "start").mockResolvedValue(undefined); + await conn.reconnect(); + expect(closeSpy).toHaveBeenCalled(); + expect(startSpy).toHaveBeenCalledWith("test-id"); + }); + }); + + describe("inputOsPassword", () => { + it("sends mouse clicks and password", async () => { + await conn.inputOsPassword("secret"); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("handleVideoFrame", () => { + it("clears msgbox on first frame", () => { + const mockDecoder = { + processFrame: vi.fn((data: any, cb: Function) => cb(false)), + frameBuffer: null, + close: vi.fn(), + }; + (conn as any)._videoDecoder = mockDecoder; + (conn as any)._firstFrame = false; + + conn.handleVideoFrame({ + vp9s: { frames: [{ data: new Uint8Array([1, 2]) }] }, + } as any); + + expect(globals.msgbox).toHaveBeenCalledWith("", "", ""); + }); + + it("draws frame when decoder succeeds", () => { + const frameBuffer = { format: { displayWidth: 1920, displayHeight: 1080 } }; + const mockDecoder = { + processFrame: vi.fn((data: any, cb: Function) => { + mockDecoder.frameBuffer = frameBuffer; + cb(true); + }), + frameBuffer: null as any, + close: vi.fn(), + }; + (conn as any)._videoDecoder = mockDecoder; + (conn as any)._firstFrame = true; + + conn.handleVideoFrame({ + vp9s: { frames: [{ data: new Uint8Array([1, 2]) }] }, + } as any); + + expect(globals.draw).toHaveBeenCalledWith(frameBuffer); + }); + + it("sends video received after processing all frames", () => { + const mockDecoder = { + processFrame: vi.fn((data: any, cb: Function) => cb(false)), + frameBuffer: null, + close: vi.fn(), + }; + (conn as any)._videoDecoder = mockDecoder; + (conn as any)._firstFrame = true; + + conn.handleVideoFrame({ + vp9s: { frames: [{ data: new Uint8Array([1]) }, { data: new Uint8Array([2]) }] }, + } as any); + + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + }); + + describe("_start", () => { + it("connects to rendezvous and handles punch_hole_response with other_failure", async () => { + nextWsResponse = { punch_hole_response: { other_failure: "Server busy" } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "Server busy"); + }); + + it("handles ID_NOT_EXIST failure", async () => { + nextWsResponse = { punch_hole_response: { failure: 0 } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "ID does not exist"); + }); + + it("handles OFFLINE failure", async () => { + nextWsResponse = { punch_hole_response: { failure: 1 } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "Remote desktop is offline"); + }); + + it("handles LICENSE_MISMATCH failure", async () => { + nextWsResponse = { punch_hole_response: { failure: 2 } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "Key mismatch"); + }); + + it("handles LICENSE_OVERUSE failure", async () => { + nextWsResponse = { punch_hole_response: { failure: 3 } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "Key overuse"); + }); + + it("handles relay_response with no version", async () => { + nextWsResponse = { relay_response: { version: 0, pk: new Uint8Array(32), uuid: "test-uuid" } }; + await (conn as any)._start("test-peer"); + expect(globals.msgbox).toHaveBeenCalledWith("error", "Error", "Remote version is low, not support web"); + }); + + it("loads stored password from options", async () => { + (conn as any)._options = { password: "1,2,3,4" }; + (conn as any)._password = undefined; + nextWsResponse = { punch_hole_response: { other_failure: "stop" } }; + await (conn as any)._start("test-peer"); + expect((conn as any)._password).toBeInstanceOf(Uint8Array); + }); + + it("handles invalid stored password gracefully", async () => { + (conn as any)._options = { password: "not-valid-array" }; + (conn as any)._password = undefined; + nextWsResponse = { punch_hole_response: { other_failure: "stop" } }; + await (conn as any)._start("test-peer"); + }); + }); + + describe("connectRelay", () => { + it("connects to relay server and starts msg loop", async () => { + const secureSpy = vi.spyOn(conn as any, "secure").mockResolvedValue(true); + const msgLoopSpy = vi.spyOn(conn as any, "msgLoop").mockResolvedValue(undefined); + + await conn.connectRelay({ + pk: new Uint8Array(32), + uuid: "test-uuid", + } as any); + + expect(secureSpy).toHaveBeenCalled(); + expect(globals.pushEvent).toHaveBeenCalledWith("connection_ready", { secure: true, direct: false }); + expect(msgLoopSpy).toHaveBeenCalled(); + }); + }); + + describe("secure", () => { + it("sends empty public key when pk is undefined", async () => { + await (conn as any).secure(undefined); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("sends empty public key when pk verification fails", async () => { + vi.mocked(globals.verify).mockRejectedValueOnce(new Error("verify failed")); + await (conn as any).secure(new Uint8Array(32)); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("establishes secure connection with valid pk", async () => { + + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(32) }); + vi.mocked(globals.verify).mockResolvedValueOnce(new Uint8Array(64)); + mockWs.next.mockResolvedValueOnce({ + signed_id: { id: new Uint8Array(32) }, + }); + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(32) }); + + const result = await (conn as any).secure(new Uint8Array(32)); + expect(result).toBe(true); + expect(mockWs.setSecretKey).toHaveBeenCalled(); + }); + + it("falls back when signed_id is missing", async () => { + + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(32) }); + vi.mocked(globals.verify).mockResolvedValueOnce(new Uint8Array(64)); + mockWs.next.mockResolvedValueOnce({}); + + await (conn as any).secure(new Uint8Array(32)); + expect(mockWs.sendMessage).toHaveBeenCalled(); + }); + + it("falls back when id doesn't match", async () => { + + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(32) }); + vi.mocked(globals.verify).mockResolvedValueOnce(new Uint8Array(64)); + mockWs.next.mockResolvedValueOnce({ + signed_id: { id: new Uint8Array(32) }, + }); + IdPk.decode.mockReturnValueOnce({ id: "wrong-id", pk: new Uint8Array(32) }); + + await (conn as any).secure(new Uint8Array(32)); + }); + + it("falls back when peer pk length is wrong", async () => { + + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(32) }); + vi.mocked(globals.verify).mockResolvedValueOnce(new Uint8Array(64)); + mockWs.next.mockResolvedValueOnce({ + signed_id: { id: new Uint8Array(32) }, + }); + IdPk.decode.mockReturnValueOnce({ id: "test-id", pk: new Uint8Array(16) }); + + await (conn as any).secure(new Uint8Array(32)); + }); + }); +}); diff --git a/flutter/web/js/src/connection.ts b/flutter/web/js/src/connection.ts index acb168e0f8a..09e55e9f2e7 100644 --- a/flutter/web/js/src/connection.ts +++ b/flutter/web/js/src/connection.ts @@ -5,38 +5,16 @@ import { loadVp9 } from "./codec"; import * as sha256 from "fast-sha256"; import * as globals from "./globals"; import { decompress, mapKey, sleep } from "./common"; +import { loadConfig, getConfigKey, getDefaultUri, getHost, getRelayHost } from "./url"; -const PORT = 21116; -let HOST = window.location.hostname; -let RELAY_HOST = ""; -let CONFIG_KEY = ""; -const SCHEMA = "ws://"; - -export async function loadConfig(): Promise { - try { - const resp = await fetch("config.json"); - if (resp.ok) { - const config = await resp.json(); - if (config.host) HOST = config.host; - if (config.relay) RELAY_HOST = config.relay; - if (config.key) CONFIG_KEY = config.key; - console.log("Loaded config: host=" + HOST + ", relay=" + (RELAY_HOST || HOST)); - } - } catch (e) { - console.log("Failed to load config.json (" + e + "), using defaults (host=" + HOST + ")"); - } -} - -export function getConfigKey(): string { - return CONFIG_KEY; -} +export { loadConfig, getConfigKey }; export function getConfigHost(): string { - return HOST; + return getHost(); } export function getConfigRelay(): string { - return RELAY_HOST; + return getRelayHost(); } type MsgboxCallback = (type: string, title: string, text: string) => void; @@ -114,7 +92,7 @@ export default class Connection { const nat_type = rendezvous.NatType.SYMMETRIC; const punch_hole_request = rendezvous.PunchHoleRequest.fromPartial({ id, - licence_key: CONFIG_KEY || undefined, + licence_key: getConfigKey() || undefined, conn_type, nat_type, token: localStorage.getItem("access_token") || undefined, @@ -157,14 +135,7 @@ export default class Connection { async connectRelay(rr: rendezvous.RelayResponse) { const pk = rr.pk; - let uri: string; - if (RELAY_HOST) { - uri = getrUriFromRs(RELAY_HOST, true); - } else if (rr.relay_server) { - uri = getrUriFromRs(rr.relay_server, true, 2); - } else { - uri = getDefaultUri(true); - } + const uri = getDefaultUri(true); const uuid = rr.uuid; console.log(new Date() + ": Connecting to relay server: " + uri); const ws = new Websock(uri, false); @@ -172,7 +143,7 @@ export default class Connection { console.log(new Date() + ": Connected to relay server"); this._ws = ws; const request_relay = rendezvous.RequestRelay.fromPartial({ - licence_key: CONFIG_KEY || undefined, + licence_key: getConfigKey() || undefined, uuid, }); ws.sendRendezvous({ request_relay }); @@ -185,7 +156,7 @@ export default class Connection { if (pk) { const RS_PK = "OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw="; try { - pk = await globals.verify(pk, CONFIG_KEY || RS_PK); + pk = await globals.verify(pk, getConfigKey() || RS_PK); if (pk) { const idpk = message.IdPk.decode(pk); if (idpk.id == this._id) { @@ -752,25 +723,6 @@ export default class Connection { } -function getDefaultUri(isRelay: Boolean = false): string { - return getrUriFromRs(HOST, isRelay); -} - -function getrUriFromRs( - uri: string, - isRelay: Boolean = false, - roffset: number = 0 -): string { - if (uri.indexOf(":") > 0) { - const tmp = uri.split(":"); - const port = parseInt(tmp[1]); - uri = tmp[0] + ":" + (port + (isRelay ? roffset || 3 : 2)); - } else { - uri += ":" + (PORT + (isRelay ? 3 : 2)); - } - return SCHEMA + uri; -} - function hash(datas: (string | Uint8Array)[]): Uint8Array { const hasher = new sha256.Hash(); datas.forEach((data) => { diff --git a/flutter/web/js/src/globals.js b/flutter/web/js/src/globals.js index 0b92564b475..36c24ed1d20 100644 --- a/flutter/web/js/src/globals.js +++ b/flutter/web/js/src/globals.js @@ -20,7 +20,11 @@ export function msgbox(type, title, text) { if (!type || (type == 'error' && !text)) return; const text2 = text.toLowerCase(); var hasRetry = checkIfRetry(type, title, text) ? 'true' : ''; - window.onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, link: '', hasRetry })); + if (window.onGlobalEvent) { + window.onGlobalEvent(JSON.stringify({ name: 'msgbox', type, title, text, link: '', hasRetry })); + } else { + console.warn('msgbox (no handler):', type, title, text); + } } function jsonfyForDart(payload) { @@ -41,7 +45,11 @@ function jsonfyForDart(payload) { export function pushEvent(name, payload) { payload = jsonfyForDart(payload); payload.name = name; - window.onGlobalEvent(JSON.stringify(payload)); + if (window.onGlobalEvent) { + window.onGlobalEvent(JSON.stringify(payload)); + } else { + console.warn('pushEvent (no handler):', name); + } } let yuvWorker; diff --git a/flutter/web/js/src/globals.setup.ts b/flutter/web/js/src/globals.setup.ts new file mode 100644 index 00000000000..4219785e15f --- /dev/null +++ b/flutter/web/js/src/globals.setup.ts @@ -0,0 +1,14 @@ +import { vi } from "vitest"; + +(globalThis as any).YUVCanvas = { + WebGLFrameSink: { isAvailable: () => false }, + attach: vi.fn(() => ({ + drawFrame: vi.fn(), + })), +}; +(globalThis as any).Worker = class MockWorker { + postMessage = vi.fn(); + onmessage: any = null; + terminate = vi.fn(); + constructor() {} +}; diff --git a/flutter/web/js/src/globals.test.ts b/flutter/web/js/src/globals.test.ts new file mode 100644 index 00000000000..d7fce9633d7 --- /dev/null +++ b/flutter/web/js/src/globals.test.ts @@ -0,0 +1,539 @@ +/** + * @vitest-environment jsdom + */ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("./connection", () => { + class MockConnection { + start = vi.fn().mockResolvedValue(undefined); + close = vi.fn(); + login = vi.fn(); + refresh = vi.fn(); + reconnect = vi.fn(); + toggleOption = vi.fn(); + setImageQuality = vi.fn(); + lockScreen = vi.fn(); + ctrlAltDel = vi.fn(); + switchDisplay = vi.fn(); + inputKey = vi.fn(); + inputString = vi.fn(); + inputMouse = vi.fn(); + setOption = vi.fn(); + inputOsPassword = vi.fn(); + getOption = vi.fn().mockReturnValue(""); + getRemember = vi.fn().mockReturnValue(false); + setRemember = vi.fn(); + getImageQuality = vi.fn().mockReturnValue("balanced"); + } + return { + default: MockConnection, + loadConfig: vi.fn().mockResolvedValue(undefined), + getConfigKey: vi.fn().mockReturnValue("testkey"), + getConfigHost: vi.fn().mockReturnValue("testhost"), + getConfigRelay: vi.fn().mockReturnValue("testrelay"), + }; +}); + +vi.mock("libsodium-wrappers", () => ({ + default: { + ready: Promise.resolve(), + crypto_sign_open: vi.fn((signed: any, pk: any) => new Uint8Array([1, 2])), + from_base64: vi.fn((s: string) => new Uint8Array([1, 2, 3])), + crypto_box_keypair: vi.fn(() => ({ + privateKey: new Uint8Array([1]), + publicKey: new Uint8Array([2]), + })), + crypto_secretbox_keygen: vi.fn(() => new Uint8Array([3])), + crypto_box_easy: vi.fn(() => new Uint8Array([4])), + crypto_secretbox_easy: vi.fn((data: any, nonce: any, key: any) => new Uint8Array([5])), + crypto_secretbox_open_easy: vi.fn((data: any, nonce: any, key: any) => new Uint8Array([6])), + base64_variants: { ORIGINAL: 0 }, + }, +})); + +vi.mock("./codec", () => ({ + loadVp9: vi.fn(), +})); + +vi.mock("./gen_js_from_hbb", () => ({ + checkIfRetry: vi.fn(() => false), + version: "1.0.0-test", +})); + +vi.mock("./common", () => ({ + initZstd: vi.fn().mockResolvedValue(undefined), + translate: vi.fn((locale: string, text: string) => text), +})); + +vi.mock("pcm-player", () => { + class MockPCMPlayer { + feed = vi.fn(); + constructor(_opts: any) {} + } + return { default: MockPCMPlayer }; +}); + +import { + isDesktop, msgbox, pushEvent, setConn, getConn, close, newConn, + verify, genBoxKeyPair, genSecretKey, seal, encrypt, decrypt, + getPeers, copyToClipboard, draw, sendOffCanvas, initAudio, playAudio, +} from "./globals"; + +describe("isDesktop", () => { + it("returns true for desktop user agent", () => { + expect(typeof isDesktop()).toBe("boolean"); + }); +}); + +describe("msgbox", () => { + it("calls onGlobalEvent when handler exists", () => { + const handler = vi.fn(); + (window as any).onGlobalEvent = handler; + msgbox("info", "Title", "Text"); + expect(handler).toHaveBeenCalledWith( + expect.stringContaining('"name":"msgbox"') + ); + delete (window as any).onGlobalEvent; + }); + + it("skips when type is empty", () => { + const handler = vi.fn(); + (window as any).onGlobalEvent = handler; + msgbox("", "Title", "Text"); + expect(handler).not.toHaveBeenCalled(); + delete (window as any).onGlobalEvent; + }); + + it("skips error type with empty text", () => { + const handler = vi.fn(); + (window as any).onGlobalEvent = handler; + msgbox("error", "Title", ""); + expect(handler).not.toHaveBeenCalled(); + delete (window as any).onGlobalEvent; + }); + + it("warns when no handler", () => { + const spy = vi.spyOn(console, "warn").mockImplementation(() => {}); + delete (window as any).onGlobalEvent; + msgbox("info", "Title", "Text"); + expect(spy).toHaveBeenCalled(); + spy.mockRestore(); + }); +}); + +describe("pushEvent", () => { + it("serializes payload and calls onGlobalEvent", () => { + const handler = vi.fn(); + (window as any).onGlobalEvent = handler; + pushEvent("test_event", { foo: "bar" }); + expect(handler).toHaveBeenCalled(); + const parsed = JSON.parse(handler.mock.calls[0][0]); + expect(parsed.name).toBe("test_event"); + expect(parsed.foo).toBe("bar"); + delete (window as any).onGlobalEvent; + }); + + it("converts Uint8Array values to bracket notation", () => { + const handler = vi.fn(); + (window as any).onGlobalEvent = handler; + pushEvent("test", { data: new Uint8Array([1, 2, 3]) }); + const parsed = JSON.parse(handler.mock.calls[0][0]); + expect(parsed.data).toBe("[1,2,3]"); + delete (window as any).onGlobalEvent; + }); +}); + +describe("setConn / getConn / close", () => { + it("sets and gets connection", () => { + const conn = { close: vi.fn() }; + setConn(conn); + expect(getConn()).toBe(conn); + }); + + it("close clears connection", () => { + const conn = { close: vi.fn() }; + setConn(conn); + close(); + expect(getConn()).toBeUndefined(); + expect(conn.close).toHaveBeenCalled(); + }); +}); + +describe("crypto functions", () => { + it("verify opens signed message", async () => { + const result = await verify(new Uint8Array([1]), "base64key"); + expect(result).toBeInstanceOf(Uint8Array); + }); + + it("genBoxKeyPair returns sk and pk", () => { + const [sk, pk] = genBoxKeyPair(); + expect(sk).toBeInstanceOf(Uint8Array); + expect(pk).toBeInstanceOf(Uint8Array); + }); + + it("genSecretKey returns key", () => { + const key = genSecretKey(); + expect(key).toBeInstanceOf(Uint8Array); + }); + + it("seal encrypts with box", () => { + const result = seal(new Uint8Array([1]), new Uint8Array([2]), new Uint8Array([3])); + expect(result).toBeInstanceOf(Uint8Array); + }); + + it("encrypt and decrypt round-trip", () => { + const key = new Uint8Array(32); + const encrypted = encrypt(new Uint8Array([1, 2, 3]), 1, key); + expect(encrypted).toBeInstanceOf(Uint8Array); + const decrypted = decrypt(encrypted, 1, key); + expect(decrypted).toBeInstanceOf(Uint8Array); + }); +}); + +describe("getPeers", () => { + beforeEach(() => { + localStorage.clear(); + }); + + it("returns empty object when no peers stored", () => { + expect(getPeers()).toEqual({}); + }); + + it("returns parsed peers from localStorage", () => { + localStorage.setItem("peers", JSON.stringify({ abc: { tm: 1, info: {} } })); + expect(getPeers()).toEqual({ abc: { tm: 1, info: {} } }); + }); + + it("returns empty object on invalid JSON", () => { + localStorage.setItem("peers", "not-json"); + expect(getPeers()).toEqual({}); + }); +}); + +describe("setByName / getByName", () => { + beforeEach(() => { + localStorage.clear(); + }); + + it("stores remote_id", () => { + (window as any).setByName("remote_id", "12345"); + expect(localStorage.getItem("remote-id")).toBe("12345"); + }); + + it("retrieves remote_id", () => { + localStorage.setItem("remote-id", "12345"); + expect((window as any).getByName("remote_id")).toBe("12345"); + }); + + it("retrieves version", () => { + expect((window as any).getByName("version")).toBe("1.0.0-test"); + }); + + it("retrieves options as JSON", () => { + const result = JSON.parse((window as any).getByName("options")); + expect(result["custom-rendezvous-server"]).toBe("testhost"); + expect(result["relay-server"]).toBe("testrelay"); + expect(result.key).toBe("testkey"); + }); + + it("stores and retrieves local options", () => { + (window as any).setByName("option", JSON.stringify({ name: "view_style", value: "original" })); + expect(localStorage.getItem("view_style")).toBe("original"); + }); + + it("returns defaults for user options", () => { + const result = (window as any).getByName("option:user:default", "view_style"); + expect(result).toBe("adaptive"); + }); + + it("returns empty for unknown getByName", () => { + expect((window as any).getByName("nonexistent")).toBe(""); + }); + + it("returns empty for session options", () => { + expect((window as any).getByName("option:session")).toBe(""); + }); + + it("returns is_using_public_server as false", () => { + expect((window as any).getByName("is_using_public_server")).toBe("false"); + }); + + it("ignores custom-rendezvous-server in setByName option", () => { + (window as any).setByName("option", JSON.stringify({ name: "custom-rendezvous-server", value: "evil" })); + expect(localStorage.getItem("custom-rendezvous-server")).toBeNull(); + }); + + it("ignores relay-server in setByName option", () => { + (window as any).setByName("option", JSON.stringify({ name: "relay-server", value: "evil" })); + expect(localStorage.getItem("relay-server")).toBeNull(); + }); + + it("ignores key in setByName option", () => { + (window as any).setByName("option", JSON.stringify({ name: "key", value: "evil" })); + expect(localStorage.getItem("key")).toBeNull(); + }); + + it("session_add_sync returns empty string", () => { + const result = (window as any).setByName("session_add_sync", ""); + expect(result).toBe(""); + }); + + it("handles close via setByName", () => { + const conn = { close: vi.fn() }; + setConn(conn); + (window as any).setByName("close", ""); + expect(conn.close).toHaveBeenCalled(); + expect(getConn()).toBeUndefined(); + }); + + it("handles refresh via setByName", () => { + const conn = newConn(); + (window as any).setByName("refresh", ""); + expect(conn.refresh).toHaveBeenCalled(); + }); + + it("handles reconnect via setByName", () => { + const conn = newConn(); + (window as any).setByName("reconnect", ""); + expect(conn.reconnect).toHaveBeenCalled(); + }); + + it("handles toggle_option via setByName", () => { + const conn = newConn(); + (window as any).setByName("toggle_option", "show-remote-cursor"); + expect(conn.toggleOption).toHaveBeenCalledWith("show-remote-cursor"); + }); + + it("handles image_quality via setByName", () => { + const conn = newConn(); + (window as any).setByName("image_quality", "best"); + expect(conn.setImageQuality).toHaveBeenCalledWith("best"); + }); + + it("handles lock_screen via setByName", () => { + const conn = newConn(); + (window as any).setByName("lock_screen", ""); + expect(conn.lockScreen).toHaveBeenCalled(); + }); + + it("handles ctrl_alt_del via setByName", () => { + const conn = newConn(); + (window as any).setByName("ctrl_alt_del", ""); + expect(conn.ctrlAltDel).toHaveBeenCalled(); + }); + + it("handles switch_display via setByName", () => { + const conn = newConn(); + (window as any).setByName("switch_display", "1"); + expect(conn.switchDisplay).toHaveBeenCalledWith("1"); + }); + + it("handles remove via setByName", () => { + localStorage.setItem("peers", JSON.stringify({ abc: { tm: 1 }, def: { tm: 2 } })); + (window as any).setByName("remove", "abc"); + const peers = JSON.parse(localStorage.getItem("peers")!); + expect(peers.abc).toBeUndefined(); + expect(peers.def).toBeDefined(); + }); + + it("handles input_key via setByName", () => { + const conn = newConn(); + (window as any).setByName("input_key", JSON.stringify({ + name: "a", down: "true", press: "false", alt: "false", ctrl: "false", shift: "false", command: "false", + })); + expect(conn.inputKey).toHaveBeenCalledWith("a", true, false, false, false, false, false); + }); + + it("handles input_string via setByName", () => { + const conn = newConn(); + (window as any).setByName("input_string", "hello"); + expect(conn.inputString).toHaveBeenCalledWith("hello"); + }); + + it("handles send_mouse down left via setByName", () => { + const conn = newConn(); + (window as any).setByName("send_mouse", JSON.stringify({ + type: "down", buttons: "left", x: "100", y: "200", + alt: "false", ctrl: "false", shift: "false", command: "false", + })); + expect(conn.inputMouse).toHaveBeenCalled(); + const args = conn.inputMouse.mock.calls[0]; + expect(args[0]).toBe((1 | (1 << 3))); + expect(args[1]).toBe(100); + expect(args[2]).toBe(200); + }); + + it("handles send_mouse up right via setByName", () => { + const conn = newConn(); + (window as any).setByName("send_mouse", JSON.stringify({ + type: "up", buttons: "right", x: "50", y: "60", + alt: "false", ctrl: "false", shift: "false", command: "false", + })); + const args = conn.inputMouse.mock.calls[0]; + expect(args[0]).toBe((2 | (2 << 3))); + }); + + it("handles send_mouse wheel via setByName", () => { + const conn = newConn(); + (window as any).setByName("send_mouse", JSON.stringify({ + type: "wheel", buttons: "wheel", x: "0", y: "0", + alt: "false", ctrl: "false", shift: "false", command: "false", + })); + const args = conn.inputMouse.mock.calls[0]; + expect(args[0]).toBe((3 | (4 << 3))); + }); + + it("handles peer_option via setByName", () => { + const conn = newConn(); + (window as any).setByName("peer_option", JSON.stringify({ name: "zoom", value: "100" })); + expect(conn.setOption).toHaveBeenCalledWith("zoom", "100"); + }); + + it("handles input_os_password via setByName", () => { + const conn = newConn(); + (window as any).setByName("input_os_password", "secret"); + expect(conn.inputOsPassword).toHaveBeenCalledWith("secret"); + }); + + it("handles option:local via setByName", () => { + (window as any).setByName("option:local", JSON.stringify({ name: "theme", value: "dark" })); + expect(localStorage.getItem("theme")).toBe("dark"); + }); + + it("handles option:user:default via setByName", () => { + (window as any).setByName("option:user:default", JSON.stringify({ name: "scroll_style", value: "scrollbar" })); + expect(localStorage.getItem("scroll_style")).toBe("scrollbar"); + }); + + it("getByName returns alternative_codecs as empty object", () => { + expect((window as any).getByName("alternative_codecs")).toBe("{}"); + }); + + it("getByName returns image_quality from connection", () => { + newConn(); + expect((window as any).getByName("image_quality")).toBe("balanced"); + }); + + it("getByName handles translate", () => { + expect((window as any).getByName("translate", JSON.stringify({ locale: "en", text: "hello" }))).toBe("hello"); + }); + + it("getByName returns peer_option from connection", () => { + newConn(); + expect((window as any).getByName("peer_option", "some_opt")).toBe(""); + }); + + it("getByName returns get_conn_status", () => { + const result = JSON.parse((window as any).getByName("get_conn_status")); + expect(result.status_num).toBe(1); + }); + + it("getByName returns option with key arg", () => { + expect((window as any).getByName("option", "key")).toBe("testkey"); + }); + + it("getByName returns option with custom-rendezvous-server arg", () => { + expect((window as any).getByName("option", "custom-rendezvous-server")).toBe("testhost"); + }); + + it("getByName returns option with relay-server arg", () => { + expect((window as any).getByName("option", "relay-server")).toBe("testrelay"); + }); + + it("getByName returns localStorage for generic option", () => { + localStorage.setItem("some-opt", "val"); + expect((window as any).getByName("option", "some-opt")).toBe("val"); + }); + + it("getByName returns option:local from localStorage", () => { + localStorage.setItem("my-local-opt", "local-val"); + expect((window as any).getByName("option:local", "my-local-opt")).toBe("local-val"); + }); + + it("getByName returns empty for option:flutter:peer", () => { + expect((window as any).getByName("option:flutter:peer")).toBe(""); + }); + + it("getByName returns empty for option:flutter:local", () => { + expect((window as any).getByName("option:flutter:local")).toBe(""); + }); + + it("getByName returns empty for envvar", () => { + expect((window as any).getByName("envvar")).toBe(""); + }); + + it("getByName returns remember from connection", () => { + newConn(); + expect((window as any).getByName("remember")).toBe("false"); + }); + + it("getByName returns toggle_option from connection", () => { + newConn(); + expect((window as any).getByName("toggle_option", "some-opt")).toBe("false"); + }); + + it("getByName returns peers sorted by tm descending", () => { + localStorage.setItem("peers", JSON.stringify({ + a: { tm: 1, info: { name: "A" } }, + b: { tm: 3, info: { name: "B" } }, + c: { tm: 2, info: { name: "C" } }, + })); + const result = JSON.parse((window as any).getByName("peers")); + expect(result[0][0]).toBe("b"); + expect(result[1][0]).toBe("c"); + expect(result[2][0]).toBe("a"); + }); + + it("getByName returns user default for scroll_style", () => { + expect((window as any).getByName("option:user:default", "scroll_style")).toBe("scrollauto"); + }); + + it("getByName returns stored value over default for user option", () => { + localStorage.setItem("view_style", "original"); + expect((window as any).getByName("option:user:default", "view_style")).toBe("original"); + }); +}); + +describe("newConn", () => { + it("creates new connection and sets it as current", () => { + const conn = newConn(); + expect(getConn()).toBe(conn); + }); + + it("closes previous connection when creating new one", () => { + const oldConn = { close: vi.fn() }; + setConn(oldConn); + newConn(); + expect(oldConn.close).toHaveBeenCalled(); + }); +}); + +describe("draw", () => { + it("posts message to yuvWorker when available", () => { + const frame = { y: { bytes: new Uint8Array(1) } }; + draw(frame); + }); +}); + +describe("copyToClipboard", () => { + it("attempts to copy text", () => { + copyToClipboard("test text"); + }); +}); + +describe("initAudio / playAudio", () => { + it("initializes audio player", () => { + initAudio(2, 48000); + }); + + it("sends audio packet to opus worker", () => { + const packet = new Uint8Array([1, 2, 3]); + playAudio(packet); + }); +}); + +describe("window.init", () => { + it("runs init sequence", async () => { + await (window as any).init(); + }); +}); diff --git a/flutter/web/js/src/url.test.ts b/flutter/web/js/src/url.test.ts new file mode 100644 index 00000000000..a9f1f716bdf --- /dev/null +++ b/flutter/web/js/src/url.test.ts @@ -0,0 +1,94 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { setConfig, getDefaultUri, getHost, getRelayHost, getConfigKey, loadConfig } from "./url"; + +describe("getDefaultUri", () => { + beforeEach(() => { + setConfig("", "", ""); + }); + + it("returns full wss:// host URL without modification", () => { + setConfig("wss://rustdesk.example.com/hbbs", "wss://rustdesk.example.com/hbbr", ""); + expect(getDefaultUri()).toBe("wss://rustdesk.example.com/hbbs"); + }); + + it("returns full wss:// relay URL without modification", () => { + setConfig("wss://rustdesk.example.com/hbbs", "wss://rustdesk.example.com/hbbr", ""); + expect(getDefaultUri(true)).toBe("wss://rustdesk.example.com/hbbr"); + }); + + it("returns full ws:// URL without modification", () => { + setConfig("ws://127.0.0.1:12022/hbbs", "ws://127.0.0.1:12022/hbbr", ""); + expect(getDefaultUri()).toBe("ws://127.0.0.1:12022/hbbs"); + expect(getDefaultUri(true)).toBe("ws://127.0.0.1:12022/hbbr"); + }); + + it("returns host:port as-is", () => { + setConfig("myserver.com:21116", "", ""); + expect(getDefaultUri()).toBe("myserver.com:21116"); + }); + + it("falls back to HOST when RELAY_HOST is empty", () => { + setConfig("wss://rustdesk.example.com/hbbs", "", ""); + expect(getDefaultUri(true)).toBe("wss://rustdesk.example.com/hbbs"); + }); + + it("returns relay when relay is set", () => { + setConfig("host.example.com:21116", "relay.example.com:21117", ""); + expect(getDefaultUri(true)).toBe("relay.example.com:21117"); + }); +}); + +describe("setConfig / getters", () => { + it("stores and retrieves host, relay, and key", () => { + setConfig("myhost", "myrelay", "mykey123"); + expect(getHost()).toBe("myhost"); + expect(getRelayHost()).toBe("myrelay"); + expect(getConfigKey()).toBe("mykey123"); + }); +}); + +describe("loadConfig", () => { + beforeEach(() => { + setConfig("", "", ""); + }); + + it("loads config from fetch response", async () => { + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ + host: "wss://test.example.com/hbbs", + relay: "wss://test.example.com/hbbr", + key: "testkey123", + }), + }); + await loadConfig(); + expect(getHost()).toBe("wss://test.example.com/hbbs"); + expect(getRelayHost()).toBe("wss://test.example.com/hbbr"); + expect(getConfigKey()).toBe("testkey123"); + }); + + it("keeps defaults when fetch fails", async () => { + setConfig("default-host", "", ""); + globalThis.fetch = vi.fn().mockRejectedValue(new Error("network error")); + await loadConfig(); + expect(getHost()).toBe("default-host"); + }); + + it("keeps defaults when response is not ok", async () => { + setConfig("default-host", "", ""); + globalThis.fetch = vi.fn().mockResolvedValue({ ok: false }); + await loadConfig(); + expect(getHost()).toBe("default-host"); + }); + + it("handles partial config (only host)", async () => { + globalThis.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ host: "partial-host" }), + }); + await loadConfig(); + expect(getHost()).toBe("partial-host"); + expect(getRelayHost()).toBe(""); + expect(getConfigKey()).toBe(""); + }); +}); diff --git a/flutter/web/js/src/url.ts b/flutter/web/js/src/url.ts new file mode 100644 index 00000000000..fdbeeff7b91 --- /dev/null +++ b/flutter/web/js/src/url.ts @@ -0,0 +1,43 @@ +let HOST = ""; +let RELAY_HOST = ""; +let CONFIG_KEY = ""; + +export function setConfig(host: string, relay: string, key: string) { + HOST = host; + RELAY_HOST = relay; + CONFIG_KEY = key; +} + +export function getHost(): string { + return HOST; +} + +export function getRelayHost(): string { + return RELAY_HOST; +} + +export function getConfigKey(): string { + return CONFIG_KEY; +} + +export function getDefaultUri(isRelay: Boolean = false): string { + if (isRelay) { + return RELAY_HOST || HOST; + } + return HOST; +} + +export async function loadConfig(): Promise { + try { + const resp = await fetch("config.json"); + if (resp.ok) { + const config = await resp.json(); + if (config.host) HOST = config.host; + if (config.relay) RELAY_HOST = config.relay; + if (config.key) CONFIG_KEY = config.key; + console.log("Loaded config: host=" + HOST + ", relay=" + (RELAY_HOST || HOST)); + } + } catch (e) { + console.log("Failed to load config.json (" + e + "), using defaults (host=" + HOST + ")"); + } +} diff --git a/flutter/web/js/src/websock.test.ts b/flutter/web/js/src/websock.test.ts new file mode 100644 index 00000000000..422651fcac6 --- /dev/null +++ b/flutter/web/js/src/websock.test.ts @@ -0,0 +1,215 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("./message.js", () => ({ + Message: { + encode: vi.fn(() => ({ finish: () => new Uint8Array([1, 2]) })), + fromPartial: vi.fn((v: any) => v), + decode: vi.fn((data: Uint8Array) => ({ decoded: true, data })), + }, +})); + +vi.mock("./rendezvous.js", () => ({ + RendezvousMessage: { + encode: vi.fn(() => ({ finish: () => new Uint8Array([3, 4]) })), + fromPartial: vi.fn((v: any) => v), + decode: vi.fn((data: Uint8Array) => ({ rendezvous: true, data })), + }, +})); + +vi.mock("./globals", () => ({ + encrypt: vi.fn((data: Uint8Array, nonce: number, key: Uint8Array) => { + const out = new Uint8Array(data.length + 1); + out.set(data); + out[data.length] = 0xee; + return out; + }), + decrypt: vi.fn((data: Uint8Array, nonce: number, key: Uint8Array) => { + return data.slice(0, data.length - 1); + }), +})); + +let mockWsInstances: any[] = []; + +class MockWebSocket { + static OPEN = 1; + static CONNECTING = 0; + static CLOSED = 3; + + url: string; + readyState = MockWebSocket.CONNECTING; + binaryType = ""; + protocol = ""; + onmessage: ((e: any) => void) | null = null; + onopen: (() => void) | null = null; + onclose: ((e: any) => void) | null = null; + onerror: ((e: any) => void) | null = null; + sent: any[] = []; + + constructor(url: string) { + this.url = url; + mockWsInstances.push(this); + } + + send(data: any) { + this.sent.push(data); + } + + close() { + this.readyState = MockWebSocket.CLOSED; + } + + simulateOpen() { + this.readyState = MockWebSocket.OPEN; + this.onopen?.(); + } + + simulateMessage(data: ArrayBuffer) { + this.onmessage?.({ data }); + } + + simulateClose(code = 1000) { + this.readyState = MockWebSocket.CLOSED; + this.onclose?.({ code }); + } + + simulateError(err: any) { + this.onerror?.(err); + } +} + +(globalThis as any).WebSocket = MockWebSocket; +(globalThis as any).window = globalThis; + +import Websock from "./websock"; + +describe("Websock", () => { + beforeEach(() => { + mockWsInstances = []; + }); + + it("creates WebSocket with given URI", () => { + const ws = new Websock("ws://test:1234"); + expect(mockWsInstances).toHaveLength(1); + expect(mockWsInstances[0].url).toBe("ws://test:1234"); + }); + + it("open resolves on successful connection", async () => { + const ws = new Websock("ws://test:1234"); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await expect(openPromise).resolves.toBe(ws); + }); + + it("open rejects on timeout", async () => { + const ws = new Websock("ws://test:1234"); + await expect(ws.open(50)).rejects.toBe("Timeout"); + }); + + it("open rejects on close before open", async () => { + const ws = new Websock("ws://test:1234"); + const openPromise = ws.open(5000); + mockWsInstances[0].simulateClose(1006); + await expect(openPromise).rejects.toBe("Reset by the peer"); + }); + + it("open rejects on error before open", async () => { + const ws = new Websock("ws://test:1234", true); + const openPromise = ws.open(5000); + mockWsInstances[0].simulateError("fail"); + await expect(openPromise).rejects.toContain("Failed to connect"); + }); + + it("latency is calculated on open", async () => { + const ws = new Websock("ws://test:1234"); + const openPromise = ws.open(1000); + await new Promise((r) => setTimeout(r, 20)); + mockWsInstances[0].simulateOpen(); + await openPromise; + expect(ws.latency()).toBeGreaterThanOrEqual(0); + }); + + it("sendRendezvous sends encoded data", async () => { + const ws = new Websock("ws://test:1234"); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + ws.sendRendezvous({ testField: true } as any); + expect(mockWsInstances[0].sent).toHaveLength(1); + }); + + it("sendMessage sends encoded data", async () => { + const ws = new Websock("ws://test:1234", false); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + ws.sendMessage({ testField: true } as any); + expect(mockWsInstances[0].sent).toHaveLength(1); + }); + + it("sendMessage encrypts when secret key is set", async () => { + const ws = new Websock("ws://test:1234", false); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + ws.setSecretKey(new Uint8Array(32)); + ws.sendMessage({ testField: true } as any); + expect(mockWsInstances[0].sent).toHaveLength(1); + }); + + it("receives and buffers rendezvous messages", async () => { + const ws = new Websock("ws://test:1234", true); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + mockWsInstances[0].simulateMessage(new Uint8Array([1, 2, 3]).buffer); + const msg = await ws.next(1000); + expect(msg).toHaveProperty("rendezvous", true); + }); + + it("receives and decrypts messages with secret key", async () => { + const ws = new Websock("ws://test:1234", false); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + ws.setSecretKey(new Uint8Array(32)); + mockWsInstances[0].simulateMessage(new Uint8Array([1, 2, 3]).buffer); + const msg = await ws.next(1000); + expect(msg).toHaveProperty("decoded", true); + }); + + it("next rejects on timeout when no messages", async () => { + const ws = new Websock("ws://test:1234", true); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + await expect(ws.next(50)).rejects.toBe("Timeout"); + }); + + it("close clears status and closes websocket", async () => { + const ws = new Websock("ws://test:1234"); + const openPromise = ws.open(1000); + mockWsInstances[0].simulateOpen(); + await openPromise; + ws.close(); + expect(mockWsInstances[0].readyState).toBe(MockWebSocket.CLOSED); + }); + + it("on/off manages event handlers", () => { + const ws = new Websock("ws://test:1234"); + const handler = vi.fn(); + ws.on("message", handler); + ws.off("message"); + }); + + it("parseMessage decodes message data", () => { + const ws = new Websock("ws://test:1234", false); + const result = ws.parseMessage(new Uint8Array([1])); + expect(result).toHaveProperty("decoded", true); + }); + + it("parseRendezvous decodes rendezvous data", () => { + const ws = new Websock("ws://test:1234", true); + const result = ws.parseRendezvous(new Uint8Array([1])); + expect(result).toHaveProperty("rendezvous", true); + }); +}); diff --git a/flutter/web/js/tsconfig.json b/flutter/web/js/tsconfig.json index 353118efcdb..777d07fc3fb 100644 --- a/flutter/web/js/tsconfig.json +++ b/flutter/web/js/tsconfig.json @@ -21,5 +21,9 @@ }, "include": [ "./src" + ], + "exclude": [ + "src/**/*.test.ts", + "src/**/*.setup.ts" ] } \ No newline at end of file diff --git a/flutter/web/js/vitest.config.ts b/flutter/web/js/vitest.config.ts new file mode 100644 index 00000000000..8ca4ff0cb74 --- /dev/null +++ b/flutter/web/js/vitest.config.ts @@ -0,0 +1,31 @@ +import { defineConfig } from "vitest/config"; +import path from "path"; + +export default defineConfig({ + resolve: { + alias: { + "./libsodium.mjs": path.resolve( + __dirname, + "node_modules/libsodium/dist/modules-esm/libsodium.mjs" + ), + }, + }, + test: { + setupFiles: ["src/globals.setup.ts"], + coverage: { + all: true, + include: ["src/**"], + exclude: [ + "src/**/*.test.ts", + "src/message.ts", + "src/rendezvous.ts", + "src/gen_js_from_hbb.ts", + "src/vite-env.d.ts", + "src/style.css", + "src/main.ts", + "src/globals.setup.ts", + "src/ui.js", + ], + }, + }, +}); diff --git a/flutter/web/js/yarn.lock b/flutter/web/js/yarn.lock index d5566487944..6a259933061 100644 --- a/flutter/web/js/yarn.lock +++ b/flutter/web/js/yarn.lock @@ -1,1494 +1,1205 @@ -# This file is generated by running "yarn install" inside your project. -# Manual changes might be lost - proceed with caution! - -__metadata: - version: 6 - cacheKey: 8 - -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 - languageName: node - linkType: hard - -"@npmcli/fs@npm:^2.1.0": - version: 2.1.0 - resolution: "@npmcli/fs@npm:2.1.0" - dependencies: - "@gar/promisify": ^1.1.3 - semver: ^7.3.5 - checksum: 6ec6d678af6da49f9dac50cd882d7f661934dd278972ffbaacde40d9eaa2871292d634000a0cca9510f6fc29855fbd4af433e1adbff90a524ec3eaf140f1219b - languageName: node - linkType: hard - -"@npmcli/move-file@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/move-file@npm:2.0.0" - dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0 - languageName: node - linkType: hard - -"@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/aspromise@npm:1.1.2" - checksum: 011fe7ef0826b0fd1a95935a033a3c0fd08483903e1aa8f8b4e0704e3233406abb9ee25350ec0c20bbecb2aad8da0dcea58b392bbd77d6690736f02c143865d2 - languageName: node - linkType: hard - -"@protobufjs/base64@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/base64@npm:1.1.2" - checksum: 67173ac34de1e242c55da52c2f5bdc65505d82453893f9b51dc74af9fe4c065cf4a657a4538e91b0d4a1a1e0a0642215e31894c31650ff6e3831471061e1ee9e - languageName: node - linkType: hard - -"@protobufjs/codegen@npm:^2.0.4": - version: 2.0.4 - resolution: "@protobufjs/codegen@npm:2.0.4" - checksum: 59240c850b1d3d0b56d8f8098dd04787dcaec5c5bd8de186fa548de86b86076e1c50e80144b90335e705a044edf5bc8b0998548474c2a10a98c7e004a1547e4b - languageName: node - linkType: hard - -"@protobufjs/eventemitter@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/eventemitter@npm:1.1.0" - checksum: 0369163a3d226851682f855f81413cbf166cd98f131edb94a0f67f79e75342d86e89df9d7a1df08ac28be2bc77e0a7f0200526bb6c2a407abbfee1f0262d5fd7 - languageName: node - linkType: hard - -"@protobufjs/fetch@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/fetch@npm:1.1.0" - dependencies: - "@protobufjs/aspromise": ^1.1.1 - "@protobufjs/inquire": ^1.1.0 - checksum: 3fce7e09eb3f1171dd55a192066450f65324fd5f7cc01a431df01bb00d0a895e6bfb5b0c5561ce157ee1d886349c90703d10a4e11a1a256418ff591b969b3477 - languageName: node - linkType: hard - -"@protobufjs/float@npm:^1.0.2": - version: 1.0.2 - resolution: "@protobufjs/float@npm:1.0.2" - checksum: 5781e1241270b8bd1591d324ca9e3a3128d2f768077a446187a049e36505e91bc4156ed5ac3159c3ce3d2ba3743dbc757b051b2d723eea9cd367bfd54ab29b2f - languageName: node - linkType: hard - -"@protobufjs/inquire@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/inquire@npm:1.1.0" - checksum: ca06f02eaf65ca36fb7498fc3492b7fc087bfcc85c702bac5b86fad34b692bdce4990e0ef444c1e2aea8c034227bd1f0484be02810d5d7e931c55445555646f4 - languageName: node - linkType: hard - -"@protobufjs/path@npm:^1.1.2": - version: 1.1.2 - resolution: "@protobufjs/path@npm:1.1.2" - checksum: 856eeb532b16a7aac071cacde5c5620df800db4c80cee6dbc56380524736205aae21e5ae47739114bf669ab5e8ba0e767a282ad894f3b5e124197cb9224445ee - languageName: node - linkType: hard - -"@protobufjs/pool@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/pool@npm:1.1.0" - checksum: d6a34fbbd24f729e2a10ee915b74e1d77d52214de626b921b2d77288bd8f2386808da2315080f2905761527cceffe7ec34c7647bd21a5ae41a25e8212ff79451 - languageName: node - linkType: hard - -"@protobufjs/utf8@npm:^1.1.0": - version: 1.1.0 - resolution: "@protobufjs/utf8@npm:1.1.0" - checksum: f9bf3163d13aaa3b6f5e6fbf37a116e094ea021c0e1f2a7ccd0e12a29e2ce08dafba4e8b36e13f8ed7397e1591610ce880ed1289af4d66cf4ace8a36a9557278 - languageName: node - linkType: hard - -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - -"@types/long@npm:^4.0.1": - version: 4.0.1 - resolution: "@types/long@npm:4.0.1" - checksum: ff9653c33f5000d0f131fd98a950a0343e2e33107dd067a97ac4a3b9678e1a2e39ea44772ad920f54ef6e8f107f76bc92c2584ba905a0dc4253282a4101166d0 - languageName: node - linkType: hard - -"@types/node@npm:>=13.7.0": - version: 17.0.8 - resolution: "@types/node@npm:17.0.8" - checksum: f4cadeb9e602027520abc88c77142697e33cf6ac98bb02f8b595a398603cbd33df1f94d01c055c9f13cde0c8eaafc5e396ca72645458d42b4318b845bc7f1d0f - languageName: node - linkType: hard - -"@types/object-hash@npm:^1.3.0": - version: 1.3.4 - resolution: "@types/object-hash@npm:1.3.4" - checksum: fe4aa041427f3c69cbcf63434af6e788329b40d7208b30aa845cfc1aa6bf9b0c11b23fa33a567d85ba7f2574a95c3b4a227f4b9b9b55da1eaea68ab94b4058d9 - languageName: node - linkType: hard - -"abbrev@npm:1": - version: 1.1.1 - resolution: "abbrev@npm:1.1.1" - checksum: a4a97ec07d7ea112c517036882b2ac22f3109b7b19077dc656316d07d308438aac28e4d9746dc4d84bf6b1e75b4a7b0a5f3cb30592419f128ca9a8cee3bcfa17 - languageName: node - linkType: hard - -"agent-base@npm:6, agent-base@npm:^6.0.2": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d - languageName: node - linkType: hard - -"agentkeepalive@npm:^4.2.1": - version: 4.2.1 - resolution: "agentkeepalive@npm:4.2.1" - dependencies: - debug: ^4.1.0 - depd: ^1.1.2 - humanize-ms: ^1.2.1 - checksum: 39cb49ed8cf217fd6da058a92828a0a84e0b74c35550f82ee0a10e1ee403c4b78ade7948be2279b188b7a7303f5d396ea2738b134731e464bf28de00a4f72a18 - languageName: node - linkType: hard - -"aggregate-error@npm:^3.0.0": - version: 3.1.0 - resolution: "aggregate-error@npm:3.1.0" - dependencies: - clean-stack: ^2.0.0 - indent-string: ^4.0.0 - checksum: 1101a33f21baa27a2fa8e04b698271e64616b886795fd43c31068c07533c7b3facfcaf4e9e0cab3624bd88f729a592f1c901a1a229c9e490eafce411a8644b79 - languageName: node - linkType: hard - -"ansi-regex@npm:^5.0.1": - version: 5.0.1 - resolution: "ansi-regex@npm:5.0.1" - checksum: 2aa4bb54caf2d622f1afdad09441695af2a83aa3fe8b8afa581d205e57ed4261c183c4d3877cee25794443fde5876417d859c108078ab788d6af7e4fe52eb66b - languageName: node - linkType: hard - -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.0 - resolution: "are-we-there-yet@npm:3.0.0" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 - languageName: node - linkType: hard - -"balanced-match@npm:^1.0.0": - version: 1.0.2 - resolution: "balanced-match@npm:1.0.2" - checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65 - languageName: node - linkType: hard - -"brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" - dependencies: - balanced-match: ^1.0.0 - concat-map: 0.0.1 - checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 - languageName: node - linkType: hard - -"brace-expansion@npm:^2.0.1": - version: 2.0.1 - resolution: "brace-expansion@npm:2.0.1" - dependencies: - balanced-match: ^1.0.0 - checksum: a61e7cd2e8a8505e9f0036b3b6108ba5e926b4b55089eeb5550cd04a471fe216c96d4fe7e4c7f995c728c554ae20ddfc4244cad10aef255e72b62930afd233d1 - languageName: node - linkType: hard - -"cacache@npm:^16.0.2": - version: 16.0.7 - resolution: "cacache@npm:16.0.7" - dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - glob: ^8.0.1 - infer-owner: ^1.0.4 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 - p-map: ^4.0.0 - promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 - tar: ^6.1.11 - unique-filename: ^1.1.1 - checksum: 2155b099b7e0f0369fb1155ca4673532ca7efe2ebdbec63acca8743580b8446b5d4fd7184626b1cb059001af77b981cdc67035c7855544d365d4f048eafca2ca - languageName: node - linkType: hard - -"chownr@npm:^2.0.0": - version: 2.0.0 - resolution: "chownr@npm:2.0.0" - checksum: c57cf9dd0791e2f18a5ee9c1a299ae6e801ff58fee96dc8bfd0dcb4738a6ce58dd252a3605b1c93c6418fe4f9d5093b28ffbf4d66648cb2a9c67eaef9679be2f - languageName: node - linkType: hard - -"clean-stack@npm:^2.0.0": - version: 2.2.0 - resolution: "clean-stack@npm:2.2.0" - checksum: 2ac8cd2b2f5ec986a3c743935ec85b07bc174d5421a5efc8017e1f146a1cf5f781ae962618f416352103b32c9cd7e203276e8c28241bbe946160cab16149fb68 - languageName: node - linkType: hard - -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - -"concat-map@npm:0.0.1": - version: 0.0.1 - resolution: "concat-map@npm:0.0.1" - checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af - languageName: node - linkType: hard - -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed - languageName: node - linkType: hard - -"dataloader@npm:^1.4.0": - version: 1.4.0 - resolution: "dataloader@npm:1.4.0" - checksum: e2c93d43afde68980efc0cd9ff48e9851116e27a9687f863e02b56d46f7e7868cc762cd6dcbaf4197e1ca850a03651510c165c2ae24b8e9843fd894002ad0e20 - languageName: node - linkType: hard - -"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.3.3": - version: 4.3.4 - resolution: "debug@npm:4.3.4" - dependencies: - ms: 2.1.2 - peerDependenciesMeta: - supports-color: - optional: true - checksum: 3dbad3f94ea64f34431a9cbf0bafb61853eda57bff2880036153438f50fb5a84f27683ba0d8e5426bf41a8c6ff03879488120cf5b3a761e77953169c0600a708 - languageName: node - linkType: hard - -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd - languageName: node - linkType: hard - -"depd@npm:^1.1.2": - version: 1.1.2 - resolution: "depd@npm:1.1.2" - checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 - languageName: node - linkType: hard - -"emoji-regex@npm:^8.0.0": - version: 8.0.0 - resolution: "emoji-regex@npm:8.0.0" - checksum: d4c5c39d5a9868b5fa152f00cada8a936868fd3367f33f71be515ecee4c803132d11b31a6222b2571b1e5f7e13890156a94880345594d0ce7e3c9895f560f192 - languageName: node - linkType: hard - -"encoding@npm:^0.1.13": - version: 0.1.13 - resolution: "encoding@npm:0.1.13" - dependencies: - iconv-lite: ^0.6.2 - checksum: bb98632f8ffa823996e508ce6a58ffcf5856330fde839ae42c9e1f436cc3b5cc651d4aeae72222916545428e54fd0f6aa8862fd8d25bdbcc4589f1e3f3715e7f - languageName: node - linkType: hard - -"env-paths@npm:^2.2.0": - version: 2.2.1 - resolution: "env-paths@npm:2.2.1" - checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e - languageName: node - linkType: hard - -"err-code@npm:^2.0.2": - version: 2.0.3 - resolution: "err-code@npm:2.0.3" - checksum: 8b7b1be20d2de12d2255c0bc2ca638b7af5171142693299416e6a9339bd7d88fc8d7707d913d78e0993176005405a236b066b45666b27b797252c771156ace54 - languageName: node - linkType: hard - -"esbuild-android-arm64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-android-arm64@npm:0.13.15" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-darwin-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-darwin-64@npm:0.13.15" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"esbuild-darwin-arm64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-darwin-arm64@npm:0.13.15" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-freebsd-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-freebsd-64@npm:0.13.15" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-freebsd-arm64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-freebsd-arm64@npm:0.13.15" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-linux-32@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-32@npm:0.13.15" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - -"esbuild-linux-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-64@npm:0.13.15" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - -"esbuild-linux-arm64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-arm64@npm:0.13.15" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - -"esbuild-linux-arm@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-arm@npm:0.13.15" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"esbuild-linux-mips64le@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-mips64le@npm:0.13.15" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - -"esbuild-linux-ppc64le@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-linux-ppc64le@npm:0.13.15" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - -"esbuild-netbsd-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-netbsd-64@npm:0.13.15" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-openbsd-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-openbsd-64@npm:0.13.15" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - -"esbuild-sunos-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-sunos-64@npm:0.13.15" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - -"esbuild-windows-32@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-windows-32@npm:0.13.15" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - -"esbuild-windows-64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-windows-64@npm:0.13.15" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - -"esbuild-windows-arm64@npm:0.13.15": - version: 0.13.15 - resolution: "esbuild-windows-arm64@npm:0.13.15" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - -"esbuild@npm:^0.13.12": - version: 0.13.15 - resolution: "esbuild@npm:0.13.15" - dependencies: - esbuild-android-arm64: 0.13.15 - esbuild-darwin-64: 0.13.15 - esbuild-darwin-arm64: 0.13.15 - esbuild-freebsd-64: 0.13.15 - esbuild-freebsd-arm64: 0.13.15 - esbuild-linux-32: 0.13.15 - esbuild-linux-64: 0.13.15 - esbuild-linux-arm: 0.13.15 - esbuild-linux-arm64: 0.13.15 - esbuild-linux-mips64le: 0.13.15 - esbuild-linux-ppc64le: 0.13.15 - esbuild-netbsd-64: 0.13.15 - esbuild-openbsd-64: 0.13.15 - esbuild-sunos-64: 0.13.15 - esbuild-windows-32: 0.13.15 - esbuild-windows-64: 0.13.15 - esbuild-windows-arm64: 0.13.15 - dependenciesMeta: - esbuild-android-arm64: - optional: true - esbuild-darwin-64: - optional: true - esbuild-darwin-arm64: - optional: true - esbuild-freebsd-64: - optional: true - esbuild-freebsd-arm64: - optional: true - esbuild-linux-32: - optional: true - esbuild-linux-64: - optional: true - esbuild-linux-arm: - optional: true - esbuild-linux-arm64: - optional: true - esbuild-linux-mips64le: - optional: true - esbuild-linux-ppc64le: - optional: true - esbuild-netbsd-64: - optional: true - esbuild-openbsd-64: - optional: true - esbuild-sunos-64: - optional: true - esbuild-windows-32: - optional: true - esbuild-windows-64: - optional: true - esbuild-windows-arm64: - optional: true - bin: - esbuild: bin/esbuild - checksum: d5fac8f28a6328592e45f9d49a7e98420cf2c2a3ff5a753bbf011ab79bcb5c062209ef862d3ae0875d8f2a50d40c112b0224e8b419a7cbffc6e2b02cee11ef7e - languageName: node - linkType: hard - -"fast-sha256@npm:^1.3.0": - version: 1.3.0 - resolution: "fast-sha256@npm:1.3.0" - checksum: 2b0bea7d3a9955e67abd2d3fbef4ce57f7dbb75708fc206d14973bd1d97aaf35b5c0a59c1d65be6f755df43d73b7657b9eac4fb3c2d58e6849966db1ef1fa186 - languageName: node - linkType: hard - -"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": - version: 2.1.0 - resolution: "fs-minipass@npm:2.1.0" - dependencies: - minipass: ^3.0.0 - checksum: 1b8d128dae2ac6cc94230cc5ead341ba3e0efaef82dab46a33d171c044caaa6ca001364178d42069b2809c35a1c3c35079a32107c770e9ffab3901b59af8c8b1 - languageName: node - linkType: hard - -"fs.realpath@npm:^1.0.0": - version: 1.0.0 - resolution: "fs.realpath@npm:1.0.0" - checksum: 99ddea01a7e75aa276c250a04eedeffe5662bce66c65c07164ad6264f9de18fb21be9433ead460e54cff20e31721c811f4fb5d70591799df5f85dce6d6746fd0 - languageName: node - linkType: hard - -"fsevents@npm:~2.3.2": - version: 2.3.2 - resolution: "fsevents@npm:2.3.2" - dependencies: - node-gyp: latest - checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" - dependencies: - node-gyp: latest - conditions: os=darwin - languageName: node - linkType: hard - -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a - languageName: node - linkType: hard - -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - -"glob@npm:^7.1.3, glob@npm:^7.1.4": - version: 7.2.3 - resolution: "glob@npm:7.2.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.1.1 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 29452e97b38fa704dabb1d1045350fb2467cf0277e155aa9ff7077e90ad81d1ea9d53d3ee63bd37c05b09a065e90f16aec4a65f5b8de401d1dac40bc5605d133 - languageName: node - linkType: hard - -"glob@npm:^8.0.1": - version: 8.0.3 - resolution: "glob@npm:8.0.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^5.0.1 - once: ^1.3.0 - checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 - languageName: node - linkType: hard - -"graceful-fs@npm:^4.2.6": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da - languageName: node - linkType: hard - -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 - languageName: node - linkType: hard - -"has@npm:^1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 - languageName: node - linkType: hard - -"http-cache-semantics@npm:^4.1.0": - version: 4.1.0 - resolution: "http-cache-semantics@npm:4.1.0" - checksum: 974de94a81c5474be07f269f9fd8383e92ebb5a448208223bfb39e172a9dbc26feff250192ecc23b9593b3f92098e010406b0f24bd4d588d631f80214648ed42 - languageName: node - linkType: hard - -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - -"iconv-lite@npm:^0.6.2": - version: 0.6.3 - resolution: "iconv-lite@npm:0.6.3" - dependencies: - safer-buffer: ">= 2.1.2 < 3.0.0" - checksum: 3f60d47a5c8fc3313317edfd29a00a692cc87a19cac0159e2ce711d0ebc9019064108323b5e493625e25594f11c6236647d8e256fbe7a58f4a3b33b89e6d30bf - languageName: node - linkType: hard - -"imurmurhash@npm:^0.1.4": - version: 0.1.4 - resolution: "imurmurhash@npm:0.1.4" - checksum: 7cae75c8cd9a50f57dadd77482359f659eaebac0319dd9368bcd1714f55e65badd6929ca58569da2b6494ef13fdd5598cd700b1eba23f8b79c5f19d195a3ecf7 - languageName: node - linkType: hard - -"indent-string@npm:^4.0.0": - version: 4.0.0 - resolution: "indent-string@npm:4.0.0" - checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 - languageName: node - linkType: hard - -"infer-owner@npm:^1.0.4": - version: 1.0.4 - resolution: "infer-owner@npm:1.0.4" - checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 - languageName: node - linkType: hard - -"inflight@npm:^1.0.4": - version: 1.0.6 - resolution: "inflight@npm:1.0.6" - dependencies: - once: ^1.3.0 - wrappy: 1 - checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd - languageName: node - linkType: hard - -"inherits@npm:2, inherits@npm:^2.0.3": - version: 2.0.4 - resolution: "inherits@npm:2.0.4" - checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 - languageName: node - linkType: hard - -"ip@npm:^1.1.5": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb - languageName: node - linkType: hard - -"is-core-module@npm:^2.8.0": - version: 2.8.1 - resolution: "is-core-module@npm:2.8.1" - dependencies: - has: ^1.0.3 - checksum: 418b7bc10768a73c41c7ef497e293719604007f88934a6ffc5f7c78702791b8528102fb4c9e56d006d69361549b3d9519440214a74aefc7e0b79e5e4411d377f - languageName: node - linkType: hard - -"is-fullwidth-code-point@npm:^3.0.0": - version: 3.0.0 - resolution: "is-fullwidth-code-point@npm:3.0.0" - checksum: 44a30c29457c7fb8f00297bce733f0a64cd22eca270f83e58c105e0d015e45c019491a4ab2faef91ab51d4738c670daff901c799f6a700e27f7314029e99e348 - languageName: node - linkType: hard - -"is-lambda@npm:^1.0.1": - version: 1.0.1 - resolution: "is-lambda@npm:1.0.1" - checksum: 93a32f01940220532e5948538699ad610d5924ac86093fcee83022252b363eb0cc99ba53ab084a04e4fb62bf7b5731f55496257a4c38adf87af9c4d352c71c35 - languageName: node - linkType: hard - -"isexe@npm:^2.0.0": - version: 2.0.0 - resolution: "isexe@npm:2.0.0" - checksum: 26bf6c5480dda5161c820c5b5c751ae1e766c587b1f951ea3fcfc973bafb7831ae5b54a31a69bd670220e42e99ec154475025a468eae58ea262f813fdc8d1c62 - languageName: node - linkType: hard - -"libsodium-wrappers@npm:^0.7.9": - version: 0.7.9 - resolution: "libsodium-wrappers@npm:0.7.9" - dependencies: - libsodium: ^0.7.0 - checksum: b5b1b9e1b4aa5662e07df244934125f9e3cd2ba7fe0ec45191a5ffc822d22f4d2f6e09e42d91c30c4f48ca0c7f810a176fdf5e32eed6722d7d82a2a719459f56 - languageName: node - linkType: hard - -"libsodium@npm:^0.7.0, libsodium@npm:^0.7.9": - version: 0.7.9 - resolution: "libsodium@npm:0.7.9" - checksum: 1c922c9972cf97ddb7207ee4f810dd291e0610dd57ea0e47f2343968392546aaa629945a2fb39ae5f19d067f6ed0bb7330f32cc9a680a847a662e9a210ce7bfb - languageName: node - linkType: hard - -"lodash@npm:^4.17.15": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 - languageName: node - linkType: hard - -"long@npm:^4.0.0": - version: 4.0.0 - resolution: "long@npm:4.0.0" - checksum: 16afbe8f749c7c849db1f4de4e2e6a31ac6e617cead3bdc4f9605cb703cd20e1e9fc1a7baba674ffcca57d660a6e5b53a9e236d7b25a295d3855cca79cc06744 - languageName: node - linkType: hard - -"lru-cache@npm:^6.0.0": - version: 6.0.0 - resolution: "lru-cache@npm:6.0.0" - dependencies: - yallist: ^4.0.0 - checksum: f97f499f898f23e4585742138a22f22526254fdba6d75d41a1c2526b3b6cc5747ef59c5612ba7375f42aca4f8461950e925ba08c991ead0651b4918b7c978297 - languageName: node - linkType: hard - -"lru-cache@npm:^7.7.1": - version: 7.10.1 - resolution: "lru-cache@npm:7.10.1" - checksum: e8b190d71ed0fcd7b29c71a3e9b01f851c92d1ef8865ff06b5581ca991db1e5e006920ed4da8b56da1910664ed51abfd76c46fb55e82ac252ff6c970ff910d72 - languageName: node - linkType: hard - -"make-fetch-happen@npm:^10.0.3": - version: 10.1.3 - resolution: "make-fetch-happen@npm:10.1.3" - dependencies: - agentkeepalive: ^4.2.1 - cacache: ^16.0.2 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 - minipass-flush: ^1.0.5 - minipass-pipeline: ^1.2.4 - negotiator: ^0.6.3 - promise-retry: ^2.0.1 - socks-proxy-agent: ^6.1.1 - ssri: ^9.0.0 - checksum: 14b9bc5fb65a1a1f53b4579c947d1ebdb18db71eb0b35a2eab612e9642a14127917528fe4ffb2c37aaa0d27dfd7507e4044e6e2e47b43985e8fa18722f535b8f - languageName: node - linkType: hard - -"minimatch@npm:^3.1.1": - version: 3.1.2 - resolution: "minimatch@npm:3.1.2" - dependencies: - brace-expansion: ^1.1.7 - checksum: c154e566406683e7bcb746e000b84d74465b3a832c45d59912b9b55cd50dee66e5c4b1e5566dba26154040e51672f9aa450a9aef0c97cfc7336b78b7afb9540a - languageName: node - linkType: hard - -"minimatch@npm:^5.0.1": - version: 5.1.0 - resolution: "minimatch@npm:5.1.0" - dependencies: - brace-expansion: ^2.0.1 - checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 - languageName: node - linkType: hard - -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" - dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 - languageName: node - linkType: hard - -"minipass-fetch@npm:^2.0.3": - version: 2.1.0 - resolution: "minipass-fetch@npm:2.1.0" - dependencies: - encoding: ^0.1.13 - minipass: ^3.1.6 - minipass-sized: ^1.0.3 - minizlib: ^2.1.2 - dependenciesMeta: - encoding: - optional: true - checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f - languageName: node - linkType: hard - -"minipass-flush@npm:^1.0.5": - version: 1.0.5 - resolution: "minipass-flush@npm:1.0.5" - dependencies: - minipass: ^3.0.0 - checksum: 56269a0b22bad756a08a94b1ffc36b7c9c5de0735a4dd1ab2b06c066d795cfd1f0ac44a0fcae13eece5589b908ecddc867f04c745c7009be0b566421ea0944cf - languageName: node - linkType: hard - -"minipass-pipeline@npm:^1.2.4": - version: 1.2.4 - resolution: "minipass-pipeline@npm:1.2.4" - dependencies: - minipass: ^3.0.0 - checksum: b14240dac0d29823c3d5911c286069e36d0b81173d7bdf07a7e4a91ecdef92cdff4baaf31ea3746f1c61e0957f652e641223970870e2353593f382112257971b - languageName: node - linkType: hard - -"minipass-sized@npm:^1.0.3": - version: 1.0.3 - resolution: "minipass-sized@npm:1.0.3" - dependencies: - minipass: ^3.0.0 - checksum: 79076749fcacf21b5d16dd596d32c3b6bf4d6e62abb43868fac21674078505c8b15eaca4e47ed844985a4514854f917d78f588fcd029693709417d8f98b2bd60 - languageName: node - linkType: hard - -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": - version: 3.1.6 - resolution: "minipass@npm:3.1.6" - dependencies: - yallist: ^4.0.0 - checksum: 57a04041413a3531a65062452cb5175f93383ef245d6f4a2961d34386eb9aa8ac11ac7f16f791f5e8bbaf1dfb1ef01596870c88e8822215db57aa591a5bb0a77 - languageName: node - linkType: hard - -"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": - version: 2.1.2 - resolution: "minizlib@npm:2.1.2" - dependencies: - minipass: ^3.0.0 - yallist: ^4.0.0 - checksum: f1fdeac0b07cf8f30fcf12f4b586795b97be856edea22b5e9072707be51fc95d41487faec3f265b42973a304fe3a64acd91a44a3826a963e37b37bafde0212c3 - languageName: node - linkType: hard - -"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": - version: 1.0.4 - resolution: "mkdirp@npm:1.0.4" - bin: - mkdirp: bin/cmd.js - checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f - languageName: node - linkType: hard - -"ms@npm:2.1.2": - version: 2.1.2 - resolution: "ms@npm:2.1.2" - checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f - languageName: node - linkType: hard - -"ms@npm:^2.0.0": - version: 2.1.3 - resolution: "ms@npm:2.1.3" - checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d - languageName: node - linkType: hard - -"nanoid@npm:^3.1.30": - version: 3.2.0 - resolution: "nanoid@npm:3.2.0" - bin: - nanoid: bin/nanoid.cjs - checksum: 3d1d5a69fea84e538057cf64106e713931c4ef32af344068ecff153ff91252f39b0f2b472e09b0dfff43ac3cf520c92938d90e6455121fe93976e23660f4fccc - languageName: node - linkType: hard - -"negotiator@npm:^0.6.3": - version: 0.6.3 - resolution: "negotiator@npm:0.6.3" - checksum: b8ffeb1e262eff7968fc90a2b6767b04cfd9842582a9d0ece0af7049537266e7b2506dfb1d107a32f06dd849ab2aea834d5830f7f4d0e5cb7d36e1ae55d021d9 - languageName: node - linkType: hard - -"node-gyp@npm:latest": - version: 9.0.0 - resolution: "node-gyp@npm:9.0.0" - dependencies: - env-paths: ^2.2.0 - glob: ^7.1.4 - graceful-fs: ^4.2.6 - make-fetch-happen: ^10.0.3 - nopt: ^5.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 - semver: ^7.3.5 - tar: ^6.1.2 - which: ^2.0.2 - bin: - node-gyp: bin/node-gyp.js - checksum: 4d8ef8860f7e4f4d86c91db3f519d26ed5cc23b48fe54543e2afd86162b4acbd14f21de42a5db344525efb69a991e021b96a68c70c6e2d5f4a5cb770793da6d3 - languageName: node - linkType: hard - -"nopt@npm:^5.0.0": - version: 5.0.0 - resolution: "nopt@npm:5.0.0" - dependencies: - abbrev: 1 - bin: - nopt: bin/nopt.js - checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f - languageName: node - linkType: hard - -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a - languageName: node - linkType: hard - -"object-hash@npm:^1.3.1": - version: 1.3.1 - resolution: "object-hash@npm:1.3.1" - checksum: fdcb957a2f15a9060e30655a9f683ba1fc25dfb8809a73d32e9634bec385a2f1d686c707ac1e5f69fb773bc12df03fb64c77ce3faeed83e35f4eb1946cb1989e - languageName: node - linkType: hard - -"once@npm:^1.3.0": - version: 1.4.0 - resolution: "once@npm:1.4.0" - dependencies: - wrappy: 1 - checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68 - languageName: node - linkType: hard - -"p-map@npm:^4.0.0": - version: 4.0.0 - resolution: "p-map@npm:4.0.0" - dependencies: - aggregate-error: ^3.0.0 - checksum: cb0ab21ec0f32ddffd31dfc250e3afa61e103ef43d957cc45497afe37513634589316de4eb88abdfd969fe6410c22c0b93ab24328833b8eb1ccc087fc0442a1c - languageName: node - linkType: hard - -"path-is-absolute@npm:^1.0.0": - version: 1.0.1 - resolution: "path-is-absolute@npm:1.0.1" - checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8 - languageName: node - linkType: hard - -"path-parse@npm:^1.0.7": - version: 1.0.7 - resolution: "path-parse@npm:1.0.7" - checksum: 49abf3d81115642938a8700ec580da6e830dde670be21893c62f4e10bd7dd4c3742ddc603fe24f898cba7eb0c6bc1777f8d9ac14185d34540c6d4d80cd9cae8a - languageName: node - linkType: hard - -"pcm-player@npm:^0.0.11": - version: 0.0.11 - resolution: "pcm-player@npm:0.0.11" - checksum: 8b02471e5788f23dbc965e577656a36c77d8a92f9481ddacac2d46c52755653e61bdb7a41698cee1a29e9df0cf8d853495b3968551b025c601ac4a7bf8139d81 - languageName: node - linkType: hard - -"picocolors@npm:^1.0.0": - version: 1.0.0 - resolution: "picocolors@npm:1.0.0" - checksum: a2e8092dd86c8396bdba9f2b5481032848525b3dc295ce9b57896f931e63fc16f79805144321f72976383fc249584672a75cc18d6777c6b757603f372f745981 - languageName: node - linkType: hard - -"postcss@npm:^8.4.5": - version: 8.4.5 - resolution: "postcss@npm:8.4.5" - dependencies: - nanoid: ^3.1.30 - picocolors: ^1.0.0 - source-map-js: ^1.0.1 - checksum: b78abdd89c10f7b48f4bdcd376104a19d6e9c7495ab521729bdb3df315af6c211360e9f06887ad3bc0ab0f61a04b91d68ea11462997c79cced58b9ccd66fac07 - languageName: node - linkType: hard - -"prettier@npm:^2.5.1": - version: 2.5.1 - resolution: "prettier@npm:2.5.1" - bin: - prettier: bin-prettier.js - checksum: 21b9408476ea1c544b0e45d51ceb94a84789ff92095abb710942d780c862d0daebdb29972d47f6b4d0f7ebbfb0ffbf56cc2cfa3e3e9d1cca54864af185b15b66 - languageName: node - linkType: hard - -"promise-inflight@npm:^1.0.1": - version: 1.0.1 - resolution: "promise-inflight@npm:1.0.1" - checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 - languageName: node - linkType: hard - -"promise-retry@npm:^2.0.1": - version: 2.0.1 - resolution: "promise-retry@npm:2.0.1" - dependencies: - err-code: ^2.0.2 - retry: ^0.12.0 - checksum: f96a3f6d90b92b568a26f71e966cbbc0f63ab85ea6ff6c81284dc869b41510e6cdef99b6b65f9030f0db422bf7c96652a3fff9f2e8fb4a0f069d8f4430359429 - languageName: node - linkType: hard - -"protobufjs@npm:^6.8.8": - version: 6.11.2 - resolution: "protobufjs@npm:6.11.2" - dependencies: - "@protobufjs/aspromise": ^1.1.2 - "@protobufjs/base64": ^1.1.2 - "@protobufjs/codegen": ^2.0.4 - "@protobufjs/eventemitter": ^1.1.0 - "@protobufjs/fetch": ^1.1.0 - "@protobufjs/float": ^1.0.2 - "@protobufjs/inquire": ^1.1.0 - "@protobufjs/path": ^1.1.2 - "@protobufjs/pool": ^1.1.0 - "@protobufjs/utf8": ^1.1.0 - "@types/long": ^4.0.1 - "@types/node": ">=13.7.0" - long: ^4.0.0 - bin: - pbjs: bin/pbjs - pbts: bin/pbts - checksum: 80e9d9610c3eb66f9eae4e44a1ae30381cedb721b7d5f635d781fe4c507e2c77bb7c879addcd1dda79733d3ae589d9e66fd18d42baf99b35df7382a0f9920795 - languageName: node - linkType: hard - -"readable-stream@npm:^3.6.0": - version: 3.6.0 - resolution: "readable-stream@npm:3.6.0" - dependencies: - inherits: ^2.0.3 - string_decoder: ^1.1.1 - util-deprecate: ^1.0.1 - checksum: d4ea81502d3799439bb955a3a5d1d808592cf3133350ed352aeaa499647858b27b1c4013984900238b0873ec8d0d8defce72469fb7a83e61d53f5ad61cb80dc8 - languageName: node - linkType: hard - -"resolve@npm:^1.20.0": - version: 1.21.0 - resolution: "resolve@npm:1.21.0" - dependencies: - is-core-module: ^2.8.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: d7d9092a5c04a048bea16c7e5a2eb605ac3e8363a0cc5644de1fde17d5028e8d5f4343aab1d99bd327b98e91a66ea83e242718150c64dfedcb96e5e7aad6c4f5 - languageName: node - linkType: hard - -"resolve@patch:resolve@^1.20.0#~builtin": - version: 1.21.0 - resolution: "resolve@patch:resolve@npm%3A1.21.0#~builtin::version=1.21.0&hash=07638b" - dependencies: - is-core-module: ^2.8.0 - path-parse: ^1.0.7 - supports-preserve-symlinks-flag: ^1.0.0 - bin: - resolve: bin/resolve - checksum: a0a4d1f7409e73190f31f901f8a619960bb3bd4ae38ba3a54c7ea7e1c87758d28a73256bb8d6a35996a903d1bf14f53883f0dcac6c571c063cb8162d813ad26e - languageName: node - linkType: hard - -"retry@npm:^0.12.0": - version: 0.12.0 - resolution: "retry@npm:0.12.0" - checksum: 623bd7d2e5119467ba66202d733ec3c2e2e26568074923bc0585b6b99db14f357e79bdedb63cab56cec47491c4a0da7e6021a7465ca6dc4f481d3898fdd3158c - languageName: node - linkType: hard - -"rimraf@npm:^3.0.2": - version: 3.0.2 - resolution: "rimraf@npm:3.0.2" - dependencies: - glob: ^7.1.3 - bin: - rimraf: bin.js - checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0 - languageName: node - linkType: hard - -"rollup@npm:^2.59.0": - version: 2.64.0 - resolution: "rollup@npm:2.64.0" - dependencies: - fsevents: ~2.3.2 - dependenciesMeta: - fsevents: - optional: true - bin: - rollup: dist/bin/rollup - checksum: dc5b28538002ed635ea54af4c2ced05c52146322c61dbe0e84f294ee62e4f232a15760fdcef9bbeb742883edf9bf093ace5389bbdd816d18b9f5740555135180 - languageName: node - linkType: hard - -"safe-buffer@npm:~5.2.0": - version: 5.2.1 - resolution: "safe-buffer@npm:5.2.1" - checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 - languageName: node - linkType: hard - -"safer-buffer@npm:>= 2.1.2 < 3.0.0": - version: 2.1.2 - resolution: "safer-buffer@npm:2.1.2" - checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 - languageName: node - linkType: hard - -"semver@npm:^7.3.5": - version: 7.3.7 - resolution: "semver@npm:7.3.7" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 - languageName: node - linkType: hard - -"set-blocking@npm:^2.0.0": - version: 2.0.0 - resolution: "set-blocking@npm:2.0.0" - checksum: 6e65a05f7cf7ebdf8b7c75b101e18c0b7e3dff4940d480efed8aad3a36a4005140b660fa1d804cb8bce911cac290441dc728084a30504d3516ac2ff7ad607b02 - languageName: node - linkType: hard - -"signal-exit@npm:^3.0.7": - version: 3.0.7 - resolution: "signal-exit@npm:3.0.7" - checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 - languageName: node - linkType: hard - -"smart-buffer@npm:^4.2.0": - version: 4.2.0 - resolution: "smart-buffer@npm:4.2.0" - checksum: b5167a7142c1da704c0e3af85c402002b597081dd9575031a90b4f229ca5678e9a36e8a374f1814c8156a725d17008ae3bde63b92f9cfd132526379e580bec8b - languageName: node - linkType: hard - -"socks-proxy-agent@npm:^6.1.1": - version: 6.2.0 - resolution: "socks-proxy-agent@npm:6.2.0" - dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 6723fd64fb50334e2b340fd0a80fd8488ffc5bc43d85b7cf1d25612044f814dd7d6ea417fd47602159941236f7f4bd15669fa5d7e1f852598a31288e1a43967b - languageName: node - linkType: hard - -"socks@npm:^2.6.2": - version: 2.6.2 - resolution: "socks@npm:2.6.2" - dependencies: - ip: ^1.1.5 - smart-buffer: ^4.2.0 - checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e - languageName: node - linkType: hard - -"source-map-js@npm:^1.0.1": - version: 1.0.1 - resolution: "source-map-js@npm:1.0.1" - checksum: 22606113d62bbd468712b0cb0c46e9a8629de7eb081049c62a04d977a211abafd7d61455617f8b78daba0b6c0c7e7c88f8c6b5aaeacffac0a6676ecf5caac5ce - languageName: node - linkType: hard - -"ssri@npm:^9.0.0": - version: 9.0.0 - resolution: "ssri@npm:9.0.0" - dependencies: - minipass: ^3.1.1 - checksum: bf33174232d07cc64e77ab1c51b55d28352273380c503d35642a19627e88a2c5f160039bb0a28608a353485075dda084dbf0390c7070f9f284559eb71d01b84b - languageName: node - linkType: hard - -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.1 - checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - -"string_decoder@npm:^1.1.1": - version: 1.3.0 - resolution: "string_decoder@npm:1.3.0" - dependencies: - safe-buffer: ~5.2.0 - checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56 - languageName: node - linkType: hard - -"strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c - languageName: node - linkType: hard - -"supports-preserve-symlinks-flag@npm:^1.0.0": - version: 1.0.0 - resolution: "supports-preserve-symlinks-flag@npm:1.0.0" - checksum: 53b1e247e68e05db7b3808b99b892bd36fb096e6fba213a06da7fab22045e97597db425c724f2bbd6c99a3c295e1e73f3e4de78592289f38431049e1277ca0ae - languageName: node - linkType: hard - -"tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.1.11 - resolution: "tar@npm:6.1.11" - dependencies: - chownr: ^2.0.0 - fs-minipass: ^2.0.0 - minipass: ^3.0.0 - minizlib: ^2.1.1 - mkdirp: ^1.0.3 - yallist: ^4.0.0 - checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f - languageName: node - linkType: hard - -"ts-poet@npm:^4.5.0": - version: 4.10.0 - resolution: "ts-poet@npm:4.10.0" - dependencies: - lodash: ^4.17.15 - prettier: ^2.5.1 - checksum: ffb3890a429f7ab59d96a7a17d9cc161bce786695af0fd77156e5779cfaeda92eaae4f15995a8c71a83cfb528d61bd2518bae19c4adec147bff8dce6f27c57d3 - languageName: node - linkType: hard - -"ts-proto-descriptors@npm:^1.2.1": - version: 1.3.1 - resolution: "ts-proto-descriptors@npm:1.3.1" - dependencies: - long: ^4.0.0 - protobufjs: ^6.8.8 - checksum: ef8acf9231375dd00cfa667c688746ae24fb8012a3875d1447cb6a6e9e0311150681719072716f58a24b1df801bcc35e56faca11ea4bac1f8146038b524b93c4 - languageName: node - linkType: hard - -"ts-proto@npm:^1.101.0": - version: 1.101.0 - resolution: "ts-proto@npm:1.101.0" - dependencies: - "@types/object-hash": ^1.3.0 - dataloader: ^1.4.0 - object-hash: ^1.3.1 - protobufjs: ^6.8.8 - ts-poet: ^4.5.0 - ts-proto-descriptors: ^1.2.1 - bin: - protoc-gen-ts_proto: protoc-gen-ts_proto - checksum: d404e34cad4fc5fb19271f7f257ff177d0ebac22ceca3b287927566a3ecda2f350b8592851d27415f6ec645525eae4ab40291ce3a6a3e151bb004478a1fe634a - languageName: node - linkType: hard - -"typescript@npm:^4.4.4": - version: 4.5.4 - resolution: "typescript@npm:4.5.4" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: 59f3243f9cd6fe3161e6150ff6bf795fc843b4234a655dbd938a310515e0d61afd1ac942799e7415e4334255e41c2c49b7dd5d9fd38a17acd25a6779ca7e0961 - languageName: node - linkType: hard - -"typescript@patch:typescript@^4.4.4#~builtin": - version: 4.5.4 - resolution: "typescript@patch:typescript@npm%3A4.5.4#~builtin::version=4.5.4&hash=bda367" - bin: - tsc: bin/tsc - tsserver: bin/tsserver - checksum: eda87927f9cfb94aca9b5e47842daf37347ad3073133e17f556fbb6c18f3493c5b551eedab0f4b26774235ddb7acbe0087250d5285f72ce6819a0891dd5a74ed - languageName: node - linkType: hard - -"unique-filename@npm:^1.1.1": - version: 1.1.1 - resolution: "unique-filename@npm:1.1.1" - dependencies: - unique-slug: ^2.0.0 - checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 - languageName: node - linkType: hard - -"unique-slug@npm:^2.0.0": - version: 2.0.2 - resolution: "unique-slug@npm:2.0.2" - dependencies: - imurmurhash: ^0.1.4 - checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a - languageName: node - linkType: hard - -"util-deprecate@npm:^1.0.1": - version: 1.0.2 - resolution: "util-deprecate@npm:1.0.2" - checksum: 474acf1146cb2701fe3b074892217553dfcf9a031280919ba1b8d651a068c9b15d863b7303cb15bd00a862b498e6cf4ad7b4a08fb134edd5a6f7641681cb54a2 - languageName: node - linkType: hard - -"vite@npm:^2.7.2": - version: 2.7.12 - resolution: "vite@npm:2.7.12" - dependencies: - esbuild: ^0.13.12 - fsevents: ~2.3.2 - postcss: ^8.4.5 - resolve: ^1.20.0 - rollup: ^2.59.0 - peerDependencies: - less: "*" - sass: "*" - stylus: "*" - dependenciesMeta: - fsevents: - optional: true - peerDependenciesMeta: - less: - optional: true - sass: - optional: true - stylus: - optional: true - bin: - vite: bin/vite.js - checksum: 56d62ae8131b02891f2dbd81f26a3ca28a02bfe390f9cb4e0c2d8dc831c2e2f8264dd3c45b14c7dd48e79d83d323a35148f92729e1f3385fae04fcd691f3f985 - languageName: node - linkType: hard - -"wasm-feature-detect@npm:^1.2.11": - version: 1.2.11 - resolution: "wasm-feature-detect@npm:1.2.11" - checksum: e7f28f5e6ca0722ba059e200c47a944ebd7570027a3ac5600b7178ee9bf950fe5280a68b5e3b5f29930407cc1214695ca10ea36a3d995d3445f4e34db58a8505 - languageName: node - linkType: hard - -"web_hbb@workspace:.": - version: 0.0.0-use.local - resolution: "web_hbb@workspace:." - dependencies: - fast-sha256: ^1.3.0 - libsodium: ^0.7.9 - libsodium-wrappers: ^0.7.9 - pcm-player: ^0.0.11 - ts-proto: ^1.101.0 - typescript: ^4.4.4 - vite: ^2.7.2 - wasm-feature-detect: ^1.2.11 - zstddec: ^0.0.2 - languageName: unknown - linkType: soft - -"which@npm:^2.0.2": - version: 2.0.2 - resolution: "which@npm:2.0.2" - dependencies: - isexe: ^2.0.0 - bin: - node-which: ./bin/node-which - checksum: 1a5c563d3c1b52d5f893c8b61afe11abc3bab4afac492e8da5bde69d550de701cf9806235f20a47b5c8fa8a1d6a9135841de2596535e998027a54589000e66d1 - languageName: node - linkType: hard - -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" - dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 - languageName: node - linkType: hard - -"wrappy@npm:1": - version: 1.0.2 - resolution: "wrappy@npm:1.0.2" - checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5 - languageName: node - linkType: hard - -"yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 - languageName: node - linkType: hard - -"zstddec@npm:^0.0.2": - version: 0.0.2 - resolution: "zstddec@npm:0.0.2" - checksum: 107334442a34590173cda03614006337712658fd043fa79f72bd486de527e2a16da474d7b20d4a171f086b334c2ad8a72afb634776d79bc2c36aee065babe31b - languageName: node - linkType: hard +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@asamuzakjp/css-color@^6.0.5": + version "6.0.7" + resolved "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.7.tgz" + integrity sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw== + dependencies: + "@csstools/css-calc" "^3.3.0" + "@csstools/css-color-parser" "^4.1.10" + "@csstools/css-parser-algorithms" "^4.0.0" + "@csstools/css-tokenizer" "^4.0.0" + lru-cache "^11.5.2" + +"@asamuzakjp/dom-selector@^8.3.0": + version "8.3.2" + resolved "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz" + integrity sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q== + dependencies: + bidi-js "^1.0.3" + css-tree "^3.2.1" + is-potential-custom-element-name "^1.0.1" + lru-cache "^11.5.2" + +"@babel/helper-string-parser@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz" + integrity sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw== + +"@babel/helper-validator-identifier@^7.29.7": + version "7.29.7" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz" + integrity sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg== + +"@babel/parser@^7.29.7": + version "7.29.8" + resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz" + integrity sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA== + dependencies: + "@babel/types" "^7.29.8" + +"@babel/types@^7.29.7", "@babel/types@^7.29.8": + version "7.29.8" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz" + integrity sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg== + dependencies: + "@babel/helper-string-parser" "^7.29.7" + "@babel/helper-validator-identifier" "^7.29.7" + +"@bcoe/v8-coverage@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz" + integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA== + +"@bramus/specificity@^2.4.2": + version "2.4.2" + resolved "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz" + integrity sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw== + dependencies: + css-tree "^3.0.0" + +"@csstools/color-helpers@^6.1.1": + version "6.1.1" + resolved "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz" + integrity sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w== + +"@csstools/css-calc@^3.3.0": + version "3.3.0" + resolved "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz" + integrity sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ== + +"@csstools/css-color-parser@^4.1.10": + version "4.2.0" + resolved "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.0.tgz" + integrity sha512-5+5LEmFuY1AjXdYhmgjTJogtQnP1evJ1zrBZGUNZ0thkpwnnmKxcHdAMn/OtFjAb25zA+jKDVYVRl+5G7rjv1A== + dependencies: + "@csstools/color-helpers" "^6.1.1" + "@csstools/css-calc" "^3.3.0" + +"@csstools/css-parser-algorithms@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz" + integrity sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w== + +"@csstools/css-syntax-patches-for-csstree@^1.1.7": + version "1.1.8" + resolved "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.8.tgz" + integrity sha512-CpMLjAvwQg3BL5S0IeqsZNMH7EQrEWi0kLKOC13ZBF0ZwERiLWlibNPJr8G1kdU3Ms/r2KiNrF81pUh2HwAHdg== + +"@csstools/css-tokenizer@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz" + integrity sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA== + +"@esbuild/linux-loong64@0.14.54": + version "0.14.54" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== + +"@exodus/bytes@^1.11.0", "@exodus/bytes@^1.15.1", "@exodus/bytes@^1.6.0": + version "1.15.1" + resolved "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz" + integrity sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q== + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.5": + version "1.5.5" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" + integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== + +"@jridgewell/trace-mapping@^0.3.31": + version "0.3.31" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" + integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@oxc-project/types@=0.146.0": + version "0.146.0" + resolved "https://registry.npmjs.org/@oxc-project/types/-/types-0.146.0.tgz" + integrity sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA== + +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz" + integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz" + integrity sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g== + +"@protobufjs/eventemitter@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz" + integrity sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg== + +"@protobufjs/fetch@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz" + integrity sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw== + dependencies: + "@protobufjs/aspromise" "^1.1.1" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz" + integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz" + integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz" + integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== + +"@protobufjs/utf8@^1.1.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz" + integrity sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug== + +"@rolldown/binding-android-arm-eabi@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.5.tgz#165b80910de7cd33f772d5b7b045b259acb7420c" + integrity sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA== + +"@rolldown/binding-android-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.5.tgz#5ed74d4b8fa56c68eb1aeb81d0d207a85b6de05c" + integrity sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig== + +"@rolldown/binding-darwin-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.5.tgz#6f27c7060e58ca03061fa7d50f9dc409bc377fe1" + integrity sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww== + +"@rolldown/binding-darwin-x64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.5.tgz#74ab897d134ede4072fdc6108f00193e6167ee28" + integrity sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A== + +"@rolldown/binding-freebsd-x64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.5.tgz#7d337f9ae4b739674e1938747118ab0676c8ef8a" + integrity sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw== + +"@rolldown/binding-linux-arm-gnueabihf@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.5.tgz#a8195dc1091ade0f912b0613ef6500941e5615f4" + integrity sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg== + +"@rolldown/binding-linux-arm64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.5.tgz#40fbbb97072b1acaa3e0e8fe9774fe524e860bba" + integrity sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA== + +"@rolldown/binding-linux-arm64-musl@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.5.tgz#3cfe8b0f7c13de29dace9a9fc6c03081164176ab" + integrity sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA== + +"@rolldown/binding-linux-ppc64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.5.tgz#3bd890d96ae29f93718aa142ed0982f2ee2978ad" + integrity sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA== + +"@rolldown/binding-linux-s390x-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.5.tgz#0959a0d5af22741d5787918e27aef70dc8602804" + integrity sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ== + +"@rolldown/binding-linux-x64-gnu@1.2.5": + version "1.2.5" + resolved "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.5.tgz" + integrity sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ== + +"@rolldown/binding-linux-x64-musl@1.2.5": + version "1.2.5" + resolved "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.5.tgz" + integrity sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA== + +"@rolldown/binding-openharmony-arm64@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.5.tgz#d096567af3f738cbe6aa858ab0a01aae9f357ef4" + integrity sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw== + +"@rolldown/binding-win32-arm64-msvc@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.5.tgz#d53b911aa4e6b547b789c07747fabab2ac8c237d" + integrity sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw== + +"@rolldown/binding-win32-x64-msvc@1.2.5": + version "1.2.5" + resolved "https://registry.yarnpkg.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.5.tgz#7bbd08cfda6a98de9b756472c772a36ebb7229bc" + integrity sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw== + +"@rolldown/pluginutils@^1.0.0": + version "1.0.1" + resolved "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz" + integrity sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw== + +"@standard-schema/spec@^1.1.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz" + integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== + +"@types/chai@^5.2.2": + version "5.2.3" + resolved "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz" + integrity sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA== + dependencies: + "@types/deep-eql" "*" + assertion-error "^2.0.1" + +"@types/deep-eql@*": + version "4.0.2" + resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" + integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== + +"@types/estree@^1.0.0": + version "1.0.9" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz" + integrity sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg== + +"@types/node@>=13.7.0": + version "26.2.0" + resolved "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz" + integrity sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg== + dependencies: + undici-types "~8.3.0" + +"@types/node@^18.19.130": + version "18.19.130" + resolved "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz" + integrity sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg== + dependencies: + undici-types "~5.26.4" + +"@vitest/coverage-v8@^4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.11.tgz" + integrity sha512-8MVGEFnJIcdGjcbfKmeq8z0pZHH0JlVtoVZH9Q/qwUp6wyFnEJUBMrw9DCaj+ra3vShGmhavjalMIhPNxZAUcw== + dependencies: + "@bcoe/v8-coverage" "^1.0.2" + "@vitest/utils" "4.1.11" + ast-v8-to-istanbul "^1.0.0" + istanbul-lib-coverage "^3.2.2" + istanbul-lib-report "^3.0.1" + istanbul-reports "^3.2.0" + magicast "^0.5.2" + obug "^2.1.1" + std-env "^4.0.0-rc.1" + tinyrainbow "^3.1.0" + +"@vitest/expect@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.11.tgz" + integrity sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw== + dependencies: + "@standard-schema/spec" "^1.1.0" + "@types/chai" "^5.2.2" + "@vitest/spy" "4.1.11" + "@vitest/utils" "4.1.11" + chai "^6.2.2" + tinyrainbow "^3.1.0" + +"@vitest/mocker@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.11.tgz" + integrity sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ== + dependencies: + "@vitest/spy" "4.1.11" + estree-walker "^3.0.3" + magic-string "^0.30.21" + +"@vitest/pretty-format@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.11.tgz" + integrity sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw== + dependencies: + tinyrainbow "^3.1.0" + +"@vitest/runner@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.11.tgz" + integrity sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw== + dependencies: + "@vitest/utils" "4.1.11" + pathe "^2.0.3" + +"@vitest/snapshot@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.11.tgz" + integrity sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog== + dependencies: + "@vitest/pretty-format" "4.1.11" + "@vitest/utils" "4.1.11" + magic-string "^0.30.21" + pathe "^2.0.3" + +"@vitest/spy@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.11.tgz" + integrity sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA== + +"@vitest/utils@4.1.11": + version "4.1.11" + resolved "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.11.tgz" + integrity sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ== + dependencies: + "@vitest/pretty-format" "4.1.11" + convert-source-map "^2.0.0" + tinyrainbow "^3.1.0" + +assertion-error@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz" + integrity sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA== + +ast-v8-to-istanbul@^1.0.0: + version "1.0.5" + resolved "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.5.tgz" + integrity sha512-UPAgKJFSEGMWSDr3LX4tqnAb4f7KGT8O40Tyx8wbYmmZ/yn58lNCm8h3svs3eXgiGd5AXxz8NDOvXWvicq+rJA== + dependencies: + "@jridgewell/trace-mapping" "^0.3.31" + estree-walker "^3.0.3" + js-tokens "^10.0.0" + +bidi-js@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz" + integrity sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw== + dependencies: + require-from-string "^2.0.2" + +case-anything@^2.1.13: + version "2.1.13" + resolved "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz" + integrity sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng== + +chai@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz" + integrity sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg== + +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + +css-tree@^3.0.0, css-tree@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz" + integrity sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA== + dependencies: + mdn-data "2.27.1" + source-map-js "^1.2.1" + +data-urls@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz" + integrity sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA== + dependencies: + whatwg-mimetype "^5.0.0" + whatwg-url "^16.0.0" + +decimal.js@^10.6.0: + version "10.6.0" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz" + integrity sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg== + +detect-libc@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz" + integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg== + +detect-libc@^2.0.3: + version "2.1.2" + resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + +dprint-node@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/dprint-node/-/dprint-node-1.0.8.tgz" + integrity sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg== + dependencies: + detect-libc "^1.0.3" + +entities@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz" + integrity sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^2.0.0: + version "2.3.2" + resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz" + integrity sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw== + +esbuild-android-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" + integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== + +esbuild-android-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== + +esbuild-darwin-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25" + integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug== + +esbuild-darwin-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== + +esbuild-freebsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== + +esbuild-freebsd-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== + +esbuild-linux-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== + +esbuild-linux-64@0.14.54: + version "0.14.54" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz" + integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== + +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + +esbuild-linux-arm@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== + +esbuild-linux-mips64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== + +esbuild-linux-ppc64le@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== + +esbuild-linux-riscv64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== + +esbuild-linux-s390x@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== + +esbuild-netbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== + +esbuild-openbsd-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== + +esbuild-sunos-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== + +esbuild-windows-32@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== + +esbuild-windows-64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== + +esbuild-windows-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== + +esbuild@^0.14.14: + version "0.14.54" + resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz" + integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA== + optionalDependencies: + "@esbuild/linux-loong64" "0.14.54" + esbuild-android-64 "0.14.54" + esbuild-android-arm64 "0.14.54" + esbuild-darwin-64 "0.14.54" + esbuild-darwin-arm64 "0.14.54" + esbuild-freebsd-64 "0.14.54" + esbuild-freebsd-arm64 "0.14.54" + esbuild-linux-32 "0.14.54" + esbuild-linux-64 "0.14.54" + esbuild-linux-arm "0.14.54" + esbuild-linux-arm64 "0.14.54" + esbuild-linux-mips64le "0.14.54" + esbuild-linux-ppc64le "0.14.54" + esbuild-linux-riscv64 "0.14.54" + esbuild-linux-s390x "0.14.54" + esbuild-netbsd-64 "0.14.54" + esbuild-openbsd-64 "0.14.54" + esbuild-sunos-64 "0.14.54" + esbuild-windows-32 "0.14.54" + esbuild-windows-64 "0.14.54" + esbuild-windows-arm64 "0.14.54" + +estree-walker@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz" + integrity sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g== + dependencies: + "@types/estree" "^1.0.0" + +expect-type@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz" + integrity sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA== + +fast-sha256@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz" + integrity sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ== + +fdir@^6.5.0: + version "6.5.0" + resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" + integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== + +fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +hasown@^2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz" + integrity sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A== + dependencies: + function-bind "^1.1.2" + +html-encoding-sniffer@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz" + integrity sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg== + dependencies: + "@exodus/bytes" "^1.6.0" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + +is-core-module@^2.16.1: + version "2.16.2" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz" + integrity sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA== + dependencies: + hasown "^2.0.3" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz" + integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg== + +istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz" + integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^4.0.0" + supports-color "^7.1.0" + +istanbul-reports@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz" + integrity sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA== + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +js-tokens@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz" + integrity sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q== + +jsdom@^30.0.1: + version "30.0.1" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz" + integrity sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA== + dependencies: + "@asamuzakjp/css-color" "^6.0.5" + "@asamuzakjp/dom-selector" "^8.3.0" + "@bramus/specificity" "^2.4.2" + "@csstools/css-syntax-patches-for-csstree" "^1.1.7" + "@exodus/bytes" "^1.15.1" + css-tree "^3.2.1" + data-urls "^7.0.0" + decimal.js "^10.6.0" + html-encoding-sniffer "^6.0.0" + is-potential-custom-element-name "^1.0.1" + lru-cache "^11.5.2" + parse5 "^8.0.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^6.0.2" + undici "^8.9.0" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^8.0.1" + whatwg-mimetype "^5.0.0" + whatwg-url "^17.1.0" + xml-name-validator "^5.0.0" + +libsodium-wrappers@^0.7.9: + version "0.7.16" + resolved "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.16.tgz" + integrity sha512-Gtr/WBx4dKjvRL1pvfwZqu7gO6AfrQ0u9vFL+kXihtHf6NfkROR8pjYWn98MFDI3jN19Ii1ZUfPR9afGiPyfHg== + dependencies: + libsodium "^0.7.16" + +libsodium@^0.7.16, libsodium@^0.7.9: + version "0.7.16" + resolved "https://registry.npmjs.org/libsodium/-/libsodium-0.7.16.tgz" + integrity sha512-3HrzSPuzm6Yt9aTYCDxYEG8x8/6C0+ag655Y7rhhWZM9PT4NpdnbqlzXhGZlDnkgR6MeSTnOt/VIyHLs9aSf+Q== + +lightningcss-android-arm64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz#9a6841f88ae50fc83502903892b41af41bc2b907" + integrity sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg== + +lightningcss-darwin-arm64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz#c0f2c31c0bfd19fa4dd3f18e957a1f1a152097d6" + integrity sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg== + +lightningcss-darwin-x64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz#cb0705965acb538c6683949ce6925fb3cdf7c361" + integrity sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ== + +lightningcss-freebsd-x64@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz#763538828b26bab2680dadafcc84ee78b0eb502b" + integrity sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg== + +lightningcss-linux-arm-gnueabihf@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz#6862e3176a331aedbdec1ed352b4d7d0dd0784de" + integrity sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ== + +lightningcss-linux-arm64-gnu@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz#c6a3a2ed15141daf6bdc2628930f8e39bdf473aa" + integrity sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg== + +lightningcss-linux-arm64-musl@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz#7fa1334971fc82845f9827df6ef8a0b20914bac6" + integrity sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ== + +lightningcss-linux-x64-gnu@1.33.0: + version "1.33.0" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz" + integrity sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg== + +lightningcss-linux-x64-musl@1.33.0: + version "1.33.0" + resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz" + integrity sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw== + +lightningcss-win32-arm64-msvc@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz#850ee1103dac989cfab50e3ac22d1a69e394e63d" + integrity sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA== + +lightningcss-win32-x64-msvc@1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz#e343ae152eed3609dc6e11949d1a3bf39a1c946f" + integrity sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA== + +lightningcss@^1.33.0: + version "1.33.0" + resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz" + integrity sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA== + dependencies: + detect-libc "^2.0.3" + optionalDependencies: + lightningcss-android-arm64 "1.33.0" + lightningcss-darwin-arm64 "1.33.0" + lightningcss-darwin-x64 "1.33.0" + lightningcss-freebsd-x64 "1.33.0" + lightningcss-linux-arm-gnueabihf "1.33.0" + lightningcss-linux-arm64-gnu "1.33.0" + lightningcss-linux-arm64-musl "1.33.0" + lightningcss-linux-x64-gnu "1.33.0" + lightningcss-linux-x64-musl "1.33.0" + lightningcss-win32-arm64-msvc "1.33.0" + lightningcss-win32-x64-msvc "1.33.0" + +long@^5.2.3, long@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/long/-/long-5.3.2.tgz" + integrity sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA== + +lru-cache@^11.5.2: + version "11.5.2" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz" + integrity sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g== + +magic-string@^0.30.21: + version "0.30.21" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz" + integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ== + dependencies: + "@jridgewell/sourcemap-codec" "^1.5.5" + +magicast@^0.5.2: + version "0.5.4" + resolved "https://registry.npmjs.org/magicast/-/magicast-0.5.4.tgz" + integrity sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w== + dependencies: + "@babel/parser" "^7.29.7" + "@babel/types" "^7.29.7" + source-map-js "^1.2.1" + +make-dir@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz" + integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw== + dependencies: + semver "^7.5.3" + +mdn-data@2.27.1: + version "2.27.1" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz" + integrity sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ== + +nanoid@^3.3.17: + version "3.3.18" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz" + integrity sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w== + +obug@^2.1.1: + version "2.1.4" + resolved "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz" + integrity sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA== + +parse5@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz" + integrity sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw== + dependencies: + entities "^8.0.0" + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +pathe@^2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz" + integrity sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w== + +pcm-player@^0.0.11: + version "0.0.11" + resolved "https://registry.npmjs.org/pcm-player/-/pcm-player-0.0.11.tgz" + integrity sha512-+FmX62jiqZa7wDCqSRQ1g3DuU6JNgpymgOLCWhmiE/Lj/M+rOUNqgNwVQX509LdA9dtBtVD3EQQUSp9JqU6upw== + +picocolors@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== + +picomatch@^4.0.3, picomatch@^4.0.4, picomatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz" + integrity sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A== + +postcss@^8.4.6, postcss@^8.5.25: + version "8.5.26" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz" + integrity sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ== + dependencies: + nanoid "^3.3.17" + picocolors "^1.1.1" + source-map-js "^1.2.1" + +protobufjs@^7.2.4: + version "7.6.5" + resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz" + integrity sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.5" + "@protobufjs/eventemitter" "^1.1.1" + "@protobufjs/fetch" "^1.1.1" + "@protobufjs/float" "^1.0.2" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.1" + "@types/node" ">=13.7.0" + long "^5.3.2" + +punycode@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve@^1.22.0: + version "1.22.12" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz" + integrity sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA== + dependencies: + es-errors "^1.3.0" + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rolldown@~1.2.1: + version "1.2.5" + resolved "https://registry.npmjs.org/rolldown/-/rolldown-1.2.5.tgz" + integrity sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA== + dependencies: + "@oxc-project/types" "=0.146.0" + "@rolldown/pluginutils" "^1.0.0" + optionalDependencies: + "@rolldown/binding-android-arm-eabi" "1.2.5" + "@rolldown/binding-android-arm64" "1.2.5" + "@rolldown/binding-darwin-arm64" "1.2.5" + "@rolldown/binding-darwin-x64" "1.2.5" + "@rolldown/binding-freebsd-x64" "1.2.5" + "@rolldown/binding-linux-arm-gnueabihf" "1.2.5" + "@rolldown/binding-linux-arm64-gnu" "1.2.5" + "@rolldown/binding-linux-arm64-musl" "1.2.5" + "@rolldown/binding-linux-ppc64-gnu" "1.2.5" + "@rolldown/binding-linux-s390x-gnu" "1.2.5" + "@rolldown/binding-linux-x64-gnu" "1.2.5" + "@rolldown/binding-linux-x64-musl" "1.2.5" + "@rolldown/binding-openharmony-arm64" "1.2.5" + "@rolldown/binding-win32-arm64-msvc" "1.2.5" + "@rolldown/binding-win32-x64-msvc" "1.2.5" + +rollup@^2.59.0: + version "2.80.0" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz" + integrity sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ== + optionalDependencies: + fsevents "~2.3.2" + +saxes@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz" + integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== + dependencies: + xmlchars "^2.2.0" + +semver@^7.5.3: + version "7.8.5" + resolved "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz" + integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== + +siginfo@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz" + integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== + +source-map-js@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== + +stackback@0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz" + integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== + +std-env@^4.0.0-rc.1: + version "4.2.0" + resolved "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz" + integrity sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + +tinybench@^2.9.0: + version "2.9.0" + resolved "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz" + integrity sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg== + +tinyexec@^1.0.2: + version "1.3.0" + resolved "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz" + integrity sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ== + +tinyglobby@^0.2.15, tinyglobby@^0.2.17: + version "0.2.17" + resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz" + integrity sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g== + dependencies: + fdir "^6.5.0" + picomatch "^4.0.4" + +tinyrainbow@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.1.tgz" + integrity sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw== + +tldts-core@^7.4.10: + version "7.4.10" + resolved "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz" + integrity sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw== + +tldts@^7.0.5: + version "7.4.10" + resolved "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz" + integrity sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog== + dependencies: + tldts-core "^7.4.10" + +tough-cookie@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz" + integrity sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA== + dependencies: + tldts "^7.0.5" + +tr46@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz" + integrity sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw== + dependencies: + punycode "^2.3.1" + +ts-poet@^6.7.0: + version "6.12.0" + resolved "https://registry.npmjs.org/ts-poet/-/ts-poet-6.12.0.tgz" + integrity sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA== + dependencies: + dprint-node "^1.0.8" + +ts-proto-descriptors@1.16.0: + version "1.16.0" + resolved "https://registry.npmjs.org/ts-proto-descriptors/-/ts-proto-descriptors-1.16.0.tgz" + integrity sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA== + dependencies: + long "^5.2.3" + protobufjs "^7.2.4" + +ts-proto@^1.181.2: + version "1.181.2" + resolved "https://registry.npmjs.org/ts-proto/-/ts-proto-1.181.2.tgz" + integrity sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg== + dependencies: + case-anything "^2.1.13" + protobufjs "^7.2.4" + ts-poet "^6.7.0" + ts-proto-descriptors "1.16.0" + +typescript@^4.4.4: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +undici-types@~8.3.0: + version "8.3.0" + resolved "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz" + integrity sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ== + +undici@^8.9.0: + version "8.10.0" + resolved "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz" + integrity sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ== + +vite@2.8: + version "2.8.6" + resolved "https://registry.npmjs.org/vite/-/vite-2.8.6.tgz" + integrity sha512-e4H0QpludOVKkmOsRyqQ7LTcMUDF3mcgyNU4lmi0B5JUbe0ZxeBBl8VoZ8Y6Rfn9eFKYtdXNPcYK97ZwH+K2ug== + dependencies: + esbuild "^0.14.14" + postcss "^8.4.6" + resolve "^1.22.0" + rollup "^2.59.0" + optionalDependencies: + fsevents "~2.3.2" + +"vite@^6.0.0 || ^7.0.0 || ^8.0.0": + version "8.2.1" + resolved "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz" + integrity sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw== + dependencies: + lightningcss "^1.33.0" + picomatch "^4.0.5" + postcss "^8.5.25" + rolldown "~1.2.1" + tinyglobby "^0.2.17" + optionalDependencies: + fsevents "~2.3.3" + +vitest@^4.1.11: + version "4.1.11" + resolved "https://registry.npmjs.org/vitest/-/vitest-4.1.11.tgz" + integrity sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw== + dependencies: + "@vitest/expect" "4.1.11" + "@vitest/mocker" "4.1.11" + "@vitest/pretty-format" "4.1.11" + "@vitest/runner" "4.1.11" + "@vitest/snapshot" "4.1.11" + "@vitest/spy" "4.1.11" + "@vitest/utils" "4.1.11" + es-module-lexer "^2.0.0" + expect-type "^1.3.0" + magic-string "^0.30.21" + obug "^2.1.1" + pathe "^2.0.3" + picomatch "^4.0.3" + std-env "^4.0.0-rc.1" + tinybench "^2.9.0" + tinyexec "^1.0.2" + tinyglobby "^0.2.15" + tinyrainbow "^3.1.0" + vite "^6.0.0 || ^7.0.0 || ^8.0.0" + why-is-node-running "^2.3.0" + +w3c-xmlserializer@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz" + integrity sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA== + dependencies: + xml-name-validator "^5.0.0" + +wasm-feature-detect@^1.2.11: + version "1.9.0" + resolved "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.9.0.tgz" + integrity sha512-zonE+xlIIYtxPy++L24ow0hAD8CICb4+FgPyROd3buyXIqsJvUEDkBgfCCoXOd1Hu3DUr0GOfnPIdcGV+YpNaA== + +webidl-conversions@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz" + integrity sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ== + +whatwg-mimetype@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz" + integrity sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw== + +whatwg-url@^16.0.0: + version "16.0.1" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz" + integrity sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw== + dependencies: + "@exodus/bytes" "^1.11.0" + tr46 "^6.0.0" + webidl-conversions "^8.0.1" + +whatwg-url@^17.1.0: + version "17.1.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz" + integrity sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw== + dependencies: + "@exodus/bytes" "^1.15.1" + tr46 "^6.0.0" + webidl-conversions "^8.0.1" + +why-is-node-running@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz" + integrity sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w== + dependencies: + siginfo "^2.0.0" + stackback "0.0.2" + +xml-name-validator@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz" + integrity sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg== + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + +zstddec@^0.0.2: + version "0.0.2" + resolved "https://registry.npmjs.org/zstddec/-/zstddec-0.0.2.tgz" + integrity sha512-DCo0oxvcvOTGP/f5FA6tz2Z6wF+FIcEApSTu0zV5sQgn9hoT5lZ9YRAKUraxt9oP7l4e8TnNdi8IZTCX6WCkwA==