diff --git a/.changeset/modernize-rpc.md b/.changeset/modernize-rpc.md new file mode 100644 index 0000000..7de4a39 --- /dev/null +++ b/.changeset/modernize-rpc.md @@ -0,0 +1,8 @@ +--- +"duplex-message": patch +"simple-electron-ipc": patch +--- + +Upgrade the workspace to pnpm 12, TypeScript 7, Vite 8 and Vitest 5. Provide explicit ESM/CommonJS package entries while retaining legacy browser distribution filenames. + +Clean up pending requests, heartbeat timers and unused Worker listeners; reject pending calls on destroy and serialization failures. Preserve caller-owned options and handlers, ignore malformed or mismatched responses, handle non-Error exceptions, recreate destroyed shared hubs, and deliver repeated storage progress updates without cleanup timers. diff --git a/.changeset/request-controls.md b/.changeset/request-controls.md new file mode 100644 index 0000000..35ac1c3 --- /dev/null +++ b/.changeset/request-controls.md @@ -0,0 +1,8 @@ +--- +"duplex-message": minor +"simple-electron-ipc": minor +--- + +Add optional local `AbortSignal` cancellation and total request deadlines using `requestTimeout`, with new `REQUEST_ABORTED` and `REQUEST_TIMEOUT` error codes. Defaults preserve unlimited handler execution. Caller controls are never serialized, and completed/failed/cancelled requests release their timers and abort listeners. + +Mark heartbeats separately from business progress so new peers can deliver the legacy heartbeat string as ordinary progress. Continue accepting legacy heartbeats, and preserve the legacy heartbeat wire shape for older callers. Worker listeners are attached only for requests that will be sent. diff --git a/.changeset/review-compatibility.md b/.changeset/review-compatibility.md new file mode 100644 index 0000000..d9d130b --- /dev/null +++ b/.changeset/review-compatibility.md @@ -0,0 +1,8 @@ +--- +"duplex-message": major +"simple-electron-ipc": major +--- + +Published declarations now require TypeScript 4.1 or later for recursive RPC result inference; compilers predating 4.1 must be upgraded. Runtime emit signatures and the legacy wire protocol remain compatible. Pending calls now reject with UNKNOWN when their hub is destroyed instead of remaining unresolved; callers must handle rejected promises. + +Preserve the legacy zero-heartbeat-timeout fallback and extensionless distribution imports. Node import/require share the same implementation and shared hubs; browser bundlers retain the ESM entry. Keep outgoing target origins independent of incoming messages, require an explicit synchronous true from Electron sender validators, safely convert unreadable handler exceptions, and consolidate pending request bookkeeping to reduce allocation and cleanup overhead. diff --git a/.changeset/runtime-performance.md b/.changeset/runtime-performance.md new file mode 100644 index 0000000..c0aa1b0 --- /dev/null +++ b/.changeset/runtime-performance.md @@ -0,0 +1,6 @@ +--- +"duplex-message": patch +"simple-electron-ipc": patch +--- + +Preserve ES2018 syntax in published artifacts and verify that syntax after packing. Reduce Promise allocation, argument copying and repeated validation in the RPC path while retaining heartbeat, cleanup and origin/sender checks. Expand browser coverage to Chromium, Firefox and WebKit and real Electron checks to older Electron 31 and current Electron 44 across desktop platforms. diff --git a/.changeset/typed-ready-origins.md b/.changeset/typed-ready-origins.md new file mode 100644 index 0000000..597331e --- /dev/null +++ b/.changeset/typed-ready-origins.md @@ -0,0 +1,10 @@ +--- +"duplex-message": minor +"simple-electron-ipc": minor +--- + +Add typed RPC clients with method-specific arguments and inferred results, plus bounded, cancellable readiness probes using an explicit ready endpoint. Existing emit APIs and wire messages remain compatible. + +Add configurable incoming Window origin restrictions, default outgoing target origins and origin-pinned replies. Existing origin behavior is preserved when no policy is configured. Add a fail-closed Electron sender/frame validator and re-export the shared RPC helpers from the Electron package. + +Replace the Electron demo with a bundled sandboxed, isolated preload and a fixed contextBridge API. Add a real Electron smoke test to CI. Rewrite package documentation around runnable setup, progress, typed calls and safe integration; update npm descriptions, keywords, repository directories and the Electron package's workspace dependency range. diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 650b70a..0000000 --- a/.eslintignore +++ /dev/null @@ -1,10 +0,0 @@ -# don't ever lint node_modules -node_modules -# don't lint build output (make sure it's set to your correct build folder name) -dist -# don't lint nyc coverage output -coverage -demo -test - -rollup.config.ts \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index b2d1585..0000000 --- a/.eslintrc.js +++ /dev/null @@ -1,38 +0,0 @@ -/** @type {import('eslint').Linter.Config} */ -module.exports = { - root: true, - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - // extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], - extends: ['airbnb-typescript'], - parserOptions: { - project: './tsconfig.json', - }, - rules: { - 'no-underscore-dangle': ['off'], - 'no-nested-ternary': ['off'], - 'no-console': ['off'], - 'import/no-extraneous-dependencies': [ - 'error', - { - devDependencies: [ - '**/vite.config.ts', - '**/build.config.ts', - '**/vitest.workspace.ts', - 'jest.config.ts', - '**/*.test.ts', - '**/*.tn.ts', - '**/*.tb.ts', - ], - }, - ], - '@typescript-eslint/comma-dangle': ['off'], - 'no-trailing-spaces': ['error', { ignoreComments: true }], - 'import/prefer-default-export': ['off'], - 'prefer-promise-reject-errors': ['off'], - '@typescript-eslint/no-throw-literal': ['off'], - '@typescript-eslint/no-unused-expressions': ['off'], - semi: 'off', - '@typescript-eslint/semi': ['error', 'never'], - }, -} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 170509a..f7e181a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,36 +1,75 @@ -# This is a basic workflow to help you get started with Actions - name: CI -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] + +permissions: + contents: read -# A workflow run is made up of one or more jobs that can run sequentially or in parallel jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on + check: runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job + strategy: + fail-fast: false + matrix: + node: [22, 24] + env: + ELECTRON_SKIP_BINARY_DOWNLOAD: '1' steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - - name: Use Node.js - uses: actions/setup-node@v1 - with: - node-version: 20 - - name: Install pnpm - run: npm install -g pnpm - - - name: install deps - run: pnpm install - - name: install webkit - run: cd packages/duplex-message && pnpm playwright install chromium - - name: build lib - run: pnpm build \ No newline at end of file + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v5 + with: + node-version: ${{ matrix.node }} + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm --filter duplex-message exec playwright install --with-deps chromium firefox webkit + - run: pnpm lint + - run: pnpm build + - run: pnpm typecheck + - run: pnpm test + - run: pnpm --filter duplex-message build:demo + - run: pnpm --filter simple-electron-ipc build:demo + - run: pnpm check:package + electron: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + electron: workspace + - os: ubuntu-latest + electron: '31.7.7' + - os: windows-latest + electron: workspace + - os: macos-latest + electron: workspace + env: + NODE_USE_ENV_PROXY: '1' + steps: + - uses: actions/checkout@v5 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v5 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - if: runner.os == 'Linux' + run: pnpm --filter duplex-message exec playwright install-deps chromium + - if: runner.os == 'Linux' + run: sudo apt-get install -y xvfb + - if: matrix.electron != 'workspace' + shell: bash + run: | + mkdir -p "$RUNNER_TEMP/electron-compat" + pnpm --dir "$RUNNER_TEMP/electron-compat" add electron@${{ matrix.electron }} --allow-build electron + - if: runner.os == 'Linux' && matrix.electron == 'workspace' + run: xvfb-run -a pnpm check:electron + - if: runner.os == 'Linux' && matrix.electron != 'workspace' + run: ELECTRON_SMOKE_PACKAGE_DIR="$RUNNER_TEMP/electron-compat" xvfb-run -a pnpm check:electron + - if: runner.os != 'Linux' + run: pnpm check:electron diff --git a/.gitignore b/.gitignore index 66bb31a..bf7f842 100644 --- a/.gitignore +++ b/.gitignore @@ -71,4 +71,5 @@ cache test-cache test/cache **/dist -__screenshots__ \ No newline at end of file +__screenshots__ +.vitest/ diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 4633091..0000000 --- a/.npmrc +++ /dev/null @@ -1,3 +0,0 @@ -# sass_binary_site="https://npm.taobao.org/mirrors/node-sass" -# ELECTRON_MIRROR="http://npm.taobao.org/mirrors/electron/" -# ELECTRON_BUILDER_BINARIES_MIRROR="http://npm.taobao.org/mirrors/electron-builder-binaries/" \ No newline at end of file diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..bae5cbf --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,13 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "ignorePatterns": ["**/dist/**", "**/coverage/**"], + "rules": { + "no-console": "off" + }, + "overrides": [ + { + "files": ["**/test/**", "**/demo/**"], + "rules": { "no-unused-vars": "off" } + } + ] +} diff --git a/README.md b/README.md index efd5922..ba83a0d 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,96 @@ -

duplex-message

-
- - github actions - - - code with typescript - -
- - -

A tinny(~3kb) utility that can makes one way message responsible
-enhance postMessage/storageEvent/electron IPC/chrome extension scripts

- -## packages -1. For browser: check [Duplex-Message](https://github.com/oe/duplex-message/tree/main/packages/duplex-message) -2. For electron: check [Simple-Electron-IPC](https://github.com/oe/duplex-message/tree/main/packages/simple-electron-ipc) - -## publish steps -1. `pnpm changeset` to create a changeset -2. `pnpm changeset version` to update the version -3. `pnpm publish -r` to publish the package \ No newline at end of file +# duplex-message + +**Promise-based RPC for iframes and Web Workers, with progress feedback.** The browser library shares one API across window messages, BroadcastChannel, storage events and page-script events. An Electron adapter uses the same request lifecycle. + +[![CI](https://github.com/oe/duplex-message/actions/workflows/main.yml/badge.svg)](https://github.com/oe/duplex-message/actions) + +Use it for file processing in Workers, embedded editor tasks, or other calls that need a result and progress updates. Typed clients check methods/arguments; optional deadlines and `AbortSignal` cancellation bound local waiting. + +| Package | Use it for | Documentation | +| --- | --- | --- | +| [duplex-message](https://www.npmjs.com/package/duplex-message) | Iframes, Workers and same-origin browser tabs; no runtime dependencies | [Browser guide](packages/duplex-message/README.md) | +| [simple-electron-ipc](https://www.npmjs.com/package/simple-electron-ipc) | Bidirectional main ↔ isolated preload RPC | [Electron guide](packages/simple-electron-ipc/README.md) | + +## Quick start + +```sh +pnpm add duplex-message +# or: npm install duplex-message +``` + +**worker.ts** + +```ts +import { PostMessageHub, READY_METHOD } from 'duplex-message' + +const hub = new PostMessageHub() +hub.on(self, 'add', (a: number, b: number) => a + b) +hub.on(self, READY_METHOD, () => true) +``` + +**main.ts** + +```ts +import { PostMessageHub, createRpcClient, waitForPeer } from 'duplex-message' + +interface WorkerApi { add(a: number, b: number): number } +const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' }) +const hub = new PostMessageHub({ requestTimeout: 5000 }) +const rpc = createRpcClient((method, ...args) => hub.emit(worker, method, ...args)) + +try { + await waitForPeer(method => hub.emit(worker, method)) + console.log(await rpc.call('add', 2, 3)) // 5 +} finally { + hub.destroy() + worker.terminate() +} +``` + +This example uses module Workers with Vite. See the browser guide for [progress and cancellation](packages/duplex-message/README.md#progress-cancellation-and-deadlines), [iframe origin restrictions](packages/duplex-message/README.md#iframes-and-origin-restrictions), [transport selection](packages/duplex-message/README.md#choose-a-transport) and [comparisons](packages/duplex-message/README.md#when-to-choose-another-approach). + +For Electron, keep Node integration disabled and context isolation enabled. The [runnable Electron demo](packages/simple-electron-ipc/demo/) uses a bundled sandboxed preload and exposes specific application methods through `contextBridge`. + +## Development + +Use Node.js 22.12+, 24, or 26+ and pnpm 12.8.1. The workspace pins TypeScript 7.0.2, Vite 8.3.2 and Vitest 5.0.3. + +```sh +npm install -g pnpm@12.8.1 +pnpm install --frozen-lockfile +pnpm --filter duplex-message exec playwright install --with-deps chromium firefox webkit +pnpm lint +pnpm build +pnpm typecheck +pnpm test +pnpm check:package +``` + +`pnpm test:unit` runs Node and mocked Electron IPC tests without a browser. `pnpm test` also exercises Chromium, Firefox and WebKit Workers, real cross-origin iframes, proxies, page-script events and storage messaging. CI checks Node 22 and 24, builds both demos, and validates packed CJS/ESM imports, declarations, UMD globals and tree shaking. Packed type checks include invalid methods/arguments and inferred return types. + +```sh +pnpm --filter duplex-message dev +pnpm --filter duplex-message build:demo +pnpm --filter simple-electron-ipc build:demo +pnpm --filter simple-electron-ipc dev +pnpm check:electron +# Headless Linux: xvfb-run -a pnpm check:electron +``` + +The separate Electron smoke test launches an actual main/preload/renderer application and checks context isolation, restricted bridge methods, bidirectional RPC, progress, local cancellation and renderer controls. CI runs Electron 31 and 44 on Linux, and Electron 44 on Windows and macOS. Set `ELECTRON_SKIP_BINARY_DOWNLOAD=1` during installation if you only need library builds/unit tests; leave it unset for the Electron demo/smoke test. + +Library output targets ES2018; packed JavaScript is checked against that syntax level. Transport APIs must exist in the runtime. `waitForPeer` and cancellation require `AbortController`/`AbortSignal`; ordinary calls do not. Legacy browser distribution filenames and the UMD global remain available; package imports use explicit `.mjs`/CommonJS entries. TypeScript emits declarations directly. Existing `emit` APIs remain available; new typed clients and ready endpoints are additive. Origin/sender filters are opt-in to preserve existing integrations; configure them explicitly for trusted peers. + +## Releasing + +```sh +pnpm changeset +pnpm changeset version +pnpm publish -r +``` + +Review changesets, validation and release versions before publishing. Cancellation ends local waiting; it does not stop peer-side work or undo effects. + +## License + +MIT. See [LICENSE](LICENSE). diff --git a/docs/rpc-benchmark.json b/docs/rpc-benchmark.json new file mode 100644 index 0000000..870fd3b --- /dev/null +++ b/docs/rpc-benchmark.json @@ -0,0 +1,219 @@ +{ + "browser": "153.0.8010.12", + "methodology": "Numeric echo; microtask clone transport and native Workers; 8 rotating paused rounds and 3 rotated isolated continuous cycles (2 rounds/cycle) with 1s warmup; concurrency 1 or 32. Concurrent figures are amortized time per call, not individual latency.", + "results": [ + { + "scenario": "clone-transport", + "version": "published", + "medianUsPerCall": 12.6, + "samplesUsPerCall": [ + 13.240000000223517, + 13.019999999925494, + 13.7, + 15.060000000149012, + 11.180000000074505, + 12.180000000074505, + 11.940000000223517, + 11.6 + ] + }, + { + "scenario": "clone-transport", + "version": "previous", + "medianUsPerCall": 13.419999999925494, + "samplesUsPerCall": [ + 17.080000000074506, + 14.720000000298024, + 13.919999999925494, + 15.359999999776482, + 12.259999999776483, + 12.919999999925494, + 12.680000000074505, + 12.6 + ] + }, + { + "scenario": "clone-transport", + "version": "current", + "medianUsPerCall": 12.309999999776483, + "samplesUsPerCall": [ + 12.980000000074506, + 14.3, + 12.939999999850988, + 13.1, + 11.6, + 11.460000000149012, + 11.679999999701977, + 11.580000000074506 + ] + }, + { + "scenario": "worker-sequential", + "version": "published", + "medianUsPerCall": 57.52000000011176, + "samplesUsPerCall": [ + 51.859999999776484, + 51.55999999977648, + 42.580000000074506, + 62.26000000014901, + 54.16000000014901, + 60.8800000000745, + 65.1800000000745, + 62.1 + ] + }, + { + "scenario": "worker-sequential", + "version": "previous", + "medianUsPerCall": 65.34999999981375, + "samplesUsPerCall": [ + 69.48000000007451, + 60.579999999701975, + 51.2, + 71.6199999999255, + 61.44000000022352, + 67.48000000007451, + 65.07999999970198, + 65.6199999999255 + ] + }, + { + "scenario": "worker-sequential", + "version": "current", + "medianUsPerCall": 60.22000000011176, + "samplesUsPerCall": [ + 65.28000000007451, + 49.03999999985099, + 52.96000000014901, + 62.7, + 50.94000000022352, + 57.74000000022352, + 64.3199999999255, + 68.35999999977648 + ] + }, + { + "scenario": "worker-32-concurrent", + "version": "published", + "medianUsPerCall": 27.306250000023283, + "samplesUsPerCall": [ + 27.25, + 26.9375, + 29.112500000046566, + 25.4375, + 27.481249999953434, + 28.006249999976717, + 27.362500000046566, + 25.168749999953434 + ] + }, + { + "scenario": "worker-32-concurrent", + "version": "previous", + "medianUsPerCall": 30.881250000034925, + "samplesUsPerCall": [ + 34.625, + 30.82500000006985, + 34.331250000046566, + 30.9375, + 33.48749999993015, + 26.612500000046566, + 30.743750000023283, + 30.350000000093132 + ] + }, + { + "scenario": "worker-32-concurrent", + "version": "current", + "medianUsPerCall": 29.209375000034925, + "samplesUsPerCall": [ + 30.48125000006985, + 32.71875, + 25.350000000093132, + 32.00624999997672, + 28.668749999953434, + 26.574999999953434, + 28.85625000006985, + 29.5625 + ] + }, + { + "scenario": "worker-steady-1", + "version": "published", + "medianUsPerCall": 64.49000000003724, + "samplesUsPerCall": [ + 64.46000000014901, + 64.51999999992549, + 55.8800000000745, + 54.68000000007451, + 66.75999999977648, + 64.71999999992549 + ] + }, + { + "scenario": "worker-steady-1", + "version": "previous", + "medianUsPerCall": 64.6, + "samplesUsPerCall": [ + 56.959999999776485, + 60.140000000223516, + 69.98000000007451, + 67.260000000149, + 61.93999999985099, + 69.94000000022352 + ] + }, + { + "scenario": "worker-steady-1", + "version": "current", + "medianUsPerCall": 67.69000000003726, + "samplesUsPerCall": [ + 69.04000000022351, + 67.7, + 69.13999999985099, + 67.6800000000745, + 65.57999999970198, + 63.420000000298025 + ] + }, + { + "scenario": "worker-steady-32", + "version": "published", + "medianUsPerCall": 36.84062500001164, + "samplesUsPerCall": [ + 39.512499999953434, + 36.668749999953434, + 37.02499999990687, + 37.01250000006985, + 28.787499999976717, + 28.962500000023283 + ] + }, + { + "scenario": "worker-steady-32", + "version": "previous", + "medianUsPerCall": 26.703125, + "samplesUsPerCall": [ + 27.862500000046566, + 26.306249999906868, + 28.59375, + 26.53125, + 26.818749999976717, + 26.587500000023283 + ] + }, + { + "scenario": "worker-steady-32", + "version": "current", + "medianUsPerCall": 25.903124999953434, + "samplesUsPerCall": [ + 26.612500000046566, + 26.043749999953434, + 25.293749999953434, + 25.324999999953434, + 25.762499999953434, + 28.46875 + ] + } + ] +} diff --git a/docs/upgrade-review.md b/docs/upgrade-review.md new file mode 100644 index 0000000..5c0ff4e --- /dev/null +++ b/docs/upgrade-review.md @@ -0,0 +1,61 @@ +# Upgrade review — 2026-10-03 + +This review compares PR #57 with main and the published `duplex-message@2.1.0` distribution. It is not a claim of zero breaking changes or universal performance improvement. + +## Compatibility + +Existing `emit` signatures and legacy request/response messages remain available. Real Chromium Workers passed old→new and new→old calls, numerical progress and handler-error propagation. New callers accept legacy heartbeats; older callers still cannot distinguish the reserved heartbeat string from business progress carrying that same string. + +The release has explicit upgrade boundaries: + +- Declarations require TypeScript 4.1+ for recursive RPC result inference. Published 2.1.0 declarations compiled under 3.9; the new declarations do not. TypeScript 4.1 and 4.4 consumer checks passed, including nested promises, union results and incorrect-call rejection. Consumers do not need TypeScript 7. Electron's own declarations may impose a newer minimum. +- `destroy()` now rejects pending calls with `UNKNOWN` instead of abandoning unresolved promises. Callers must handle promise rejections. +- `signal` and `requestTimeout` in method configuration are reserved local controls; they are not forwarded as custom wire metadata. +- Workspace development requires pnpm 12 and a supported modern Node version. This is separate from the library output's ES2018 target. + +The initial upgraded build emitted ES2020 syntax unlike the published 2.1.0 artifact. Both library targets now preserve ES2018, and packed `.js`/`.mjs`/`.cjs` files are parsed with Acorn's ES2018 grammar. This checks syntax, not missing runtime APIs. The chosen transport must exist in the runtime. `waitForPeer` and cancellation require `AbortController`/`AbortSignal`; ordinary calls do not. + +A major changeset records these boundaries. Versions have not been bumped and packages have not been published. + +## Confirmed issues fixed during review + +| Issue | Result | +| --- | --- | +| Accepted incoming origins could override the configured default outgoing origin. | Removed the peer-origin cache; only replies/progress inherit their own request's verified origin. | +| Truthy non-boolean or async Electron validators could accept IPC. | Only synchronous `true` accepts a message. Accidentally rejected async validators are consumed without an unhandled rejection. | +| Unreadable thrown objects could fail inside error conversion and leave RPC unresolved. | Guarded conversion returns a serializable handler error. | +| `heartbeatTimeout: 0` changed from a 500ms fallback to immediate expiry. | Restored the legacy fallback and added a regression test. | +| Adding package exports broke valid extensionless distribution imports. | Added explicit aliases and packed-consumer checks. | +| Native Node import/require loaded different classes and shared hubs. | Both modes share the CJS implementation; Electron uses a small explicit ESM forwarding entry. Browser bundlers retain ESM. | + +## Performance and implementation size + +Request bookkeeping now uses one pending-request record plus a peer-count map, replacing five separate per-request maps. Cleanup is idempotent and removes state before invoking hooks. Origin metadata is allocated only when an origin policy is enabled and recorded only for incoming requests. Readiness is explicit and optional; unused readiness code is removed by tree shaking. No application request is automatically retried. + +The dispatch path now reuses a resolved Promise for response/progress events, avoids redundant async wrappers in Electron, handles a single endpoint without the multi-handler race machinery, and avoids argument/configuration copies when unnecessary. Thenables are assimilated once; multi-handler first-defined/all-undefined/error behavior and rejected-Promise semantics for subclass hooks are preserved by tests. + +- Unlike 2.1.0, successful calls release heartbeat timers immediately. A regression test verifies that 1,000 completed calls leave no timers or pending callbacks. A separate browser timer-count probe around 1,000 concurrent clone-transport echoes observed 1,000 timers still waiting in published 2.1.0 versus zero in this build, before destroying either hub. Cancellation/deadlines likewise release local resources; remote handlers continue. +- Full production UMD gzip size grows from approximately **3.8 KB to 5.8 KB**. Safety checks, lifecycle cleanup and optional helpers have a real size cost; this is not a package-size reduction. +- `scripts/benchmark-rpc.mjs` compares a published production ESM artifact, an optional prior PR artifact and the current build. It measures clone/microtask transport, native Worker sequential calls and 32 concurrent lanes. It rotates version order, drains baseline timers between paused measurements, and isolates continuous runs in separate contexts. Concurrent figures are amortized time per call, not individual call latency. Results below are diagnostic; scheduling variation prevents a universal speed claim. + +Measurements on 2026-10-03, Chromium 153.0.8010.12 on this Linux workspace, numerical echo; medians in µs/call. “Prior PR” is commit `2d6a57e`, before the dispatch optimization. The first three rows use 8 rotated paused rounds; steady rows use 3 rotated isolated cycles with 2 measured rounds/cycle after 1s continuous warmup. + +| Scenario | Published 2.1.0 | Prior PR | Optimized PR | +| --- | ---: | ---: | ---: | +| clone-transport | 12.60 | 13.42 | 12.31 | +| worker-sequential | 57.52 | 65.35 | 60.22 | +| worker-32-concurrent | 27.31 | 30.88 | 29.21 | +| worker-steady-1 | 64.49 | 64.60 | 67.69 | +| worker-steady-32 | 36.84 | 26.70 | 25.90 | + +Samples fluctuate with worker scheduling. Compare these workloads only; do not interpret the concurrent rows as per-call latency or claim a universal speedup. The optimized path reduces known allocation and timer-retention costs, but compatibility and security checks still carry overhead. Large payloads and other engines/platforms require separate performance measurements. + +Reproduce with `node scripts/benchmark-rpc.mjs /path/to/unpacked/duplex-message-2.1.0/dist/index.production.es.js [prior-build.mjs]`. Run without other CPU-heavy jobs. Raw samples are in [rpc-benchmark.json](rpc-benchmark.json). + +The typed client stays a small function wrapper. Readiness adds a bounded opt-in probe loop and abort cleanup, without a proxy layer, connection manager, business retry policy or global background polling. The redundant origin cache and fragmented pending-state maps were unnecessary and have been removed. + +## Validation and remaining limits + +266 tests: 81 Node, 56 each in Chromium/Firefox/WebKit, and 17 mocked Electron IPC tests. Lint, strict type checking, library builds and packed NodeNext consumers pass. Package checks cover native import/require class and singleton identity, typed RPC results, negative argument tests, extensionless imports, UMD globals and unused-feature tree shaking. Local real Electron 31 and 44 smoke tests pass with context isolation and the renderer sandbox enabled. CI runs Electron 31/44 on Linux and Electron 44 on Windows/macOS. + +The browser matrix covers current Playwright Chromium, Firefox and WebKit. WebKit testing is not a guarantee for every Safari/iOS version. Older browsers, embedded WebViews, mobile devices and Electron versions outside the matrix remain unverified. Incoming-origin and Electron-sender restrictions remain opt-in for legacy compatibility; applications must configure them and validate their arguments/permissions. Readiness requires an explicit peer endpoint. Local cancellation does not cancel or reverse peer-side work. diff --git a/package.json b/package.json index 14f7595..df1a582 100644 --- a/package.json +++ b/package.json @@ -4,13 +4,12 @@ "description": "makes one way message responsive, enhance postMessage/storageEvent/electron IPC/chrome extension scripts", "scripts": { "build": "pnpm -r build", - "test": "pnpm -r test -- run", - "lint": "eslint . --ext .ts" - }, - "husky": { - "hooks": { - "pre-commit": "pnpm lint" - } + "typecheck": "pnpm -r typecheck", + "test": "pnpm -r test", + "test:unit": "pnpm --filter duplex-message exec vitest run --project unit && pnpm --filter simple-electron-ipc test", + "lint": "oxlint packages scripts --deny-warnings", + "check:package": "node scripts/check-package.mjs", + "check:electron": "pnpm build && pnpm --filter simple-electron-ipc build:demo && node scripts/check-electron.mjs" }, "repository": "https://oe@github.com/oe/duplex-message.git", "author": "Saiya", @@ -18,22 +17,16 @@ "homepage": "https://github.com/oe/duplex-message", "private": true, "devDependencies": { - "@changesets/cli": "^2.29.2", - "@typescript-eslint/eslint-plugin": "^4.22.1", - "@typescript-eslint/parser": "^4.22.1", - "cross-env": "^5.2.0", - "eslint": "^7.25.0", - "eslint-config-airbnb-typescript": "^12.3.1", - "eslint-plugin-import": "^2.22.0", - "eslint-plugin-jsx-a11y": "^6.3.1", - "eslint-plugin-react": "^7.20.3", - "eslint-plugin-react-hooks": "^4.0.8", - "husky": "^1.1.3" - }, - "pnpm": { - "overrides": { - "typescript": "5.5.3" - } + "@changesets/cli": "3.0.3", + "@types/node": "^24.0.0", + "acorn": "8.18.0", + "oxlint": "1.86.0", + "typescript": "catalog:" }, - "packageManager": "pnpm@9.5.0" + "packageManager": "pnpm@12.8.1", + "type": "module", + "engines": { + "node": "^22.12.0 || ^24.0.0 || >=26.0.0", + "pnpm": "12.x" + } } diff --git a/packages/duplex-message/README.md b/packages/duplex-message/README.md index 23f7a0d..4a9f210 100644 --- a/packages/duplex-message/README.md +++ b/packages/duplex-message/README.md @@ -1,617 +1,313 @@ -

- -
- - github actions - - - code with typescript - - - code coverage - - - npm version - - - npm downloads - -
- - -

A tinny(~4kb) utility that can simplify cross window / iframes / workers communications, even with progress feedback support.

- ->[!NOTE] -> This utility is designed to simplify the communication between different window/node contexts(windows, iframes, workers, etc) in the browser, it's a peer-to-peer communication tool, that means you can send messages to a peer and get its response, or listen to messages from a peer and respond to it, but it's not a traditional pub/sub tool, you won't be able to listen message from it self. - -## 📖Table of Contents -- [Features](#features) -- [Install](#install) -- [Example](#example) -- [Usage](#usage) - - [Import \& Debug](#import--debug) - - [constructor](#constructor) - - [Shared instance](#shared-instance) - - [on](#on) - - [emit](#emit) - - [off](#off) - - [destroy](#destroy) - - [progress](#progress) - - [proxy for PostMessageHub](#proxy-for-postmessagehub) - - [dedicated instance for PostMessageHub](#dedicated-instance-for-postmessagehub) - - [Error](#error) -- [FAQ](#faq) - - [How to deal with the error `no corresponding handler found for method xxx`?](#how-to-deal-with-the-error-no-corresponding-handler-found-for-method-xxx) -- [Development](#development) -- [License](#license) - - -## Features -* **Simple API**: `on` `emit` and `off` are all you need -* **Responsible**: `emit` will return a promise with the response from the other side -* **Progress feedback**: get response with progress feedback easily -* **Multi-scenario**: builtin [`PostMessageHub`](#postmessagehub) [`StorageMessageHub`](#storagemessagehub) and [`PageScriptMessageHub`](#pagescriptmessagehub) for different scenarios -* **Tinny**: less than 3kb gzipped(even smaller with tree-shaking), no external dependencies required -* **Consistency**: same api every where -* **Typescript support**: this utility is written in typescript, has type definition inborn - -It also has an electron version that can simplify IPC messaging, check [Simple-Electron-IPC -](https://github.com/oe/duplex-message/tree/master/packages/simple-electron-ipc) for more details. +# duplex-message -## Install -using yarn -```sh -yarn add duplex-message -``` - -or npm -```sh -npm install duplex-message -S -``` +Promise-based RPC for **iframes, Web Workers and browser tabs**, with progress feedback, typed calls and local cancellation. -## Example +[![CI](https://github.com/oe/duplex-message/actions/workflows/main.yml/badge.svg)](https://github.com/oe/duplex-message/actions) +[![npm](https://img.shields.io/npm/v/duplex-message.svg)](https://www.npmjs.com/package/duplex-message) -The following example shows you how to use it to make normal window and its iframe communicate easily +Use it when a task in another context needs to return a result and report progress: parsing a file in a Worker, exporting from an embedded editor, or coordinating browser tabs. Four built-in transports share `on`, `emit` and `off`; the browser package has no runtime dependencies and supports tree shaking. -**in main window** -```js -import { PostMessageHub } from "duplex-message" - -const postMessageHub = new PostMessageHub() - -// get child iframe's window, peerWin could be `self.parent` or `new Worker('./worker.js')` in other situation -const peerWin = document.getElementById('child-iframe-1').contentWindow - -// ----- listen messages from peer ---- - -// listen the message pageTitle from peerWin, and respond it -postMessageHub.on(peerWin, 'pageTitle', () => { - return document.title -}) -// respond to message getHead from peerWin -postMessageHub.on(peerWin, 'add', async (a, b) => { - return new Promise((resolve, reject) => { - setTimeout(() => { - resolve(a + b) - }, 1000) - }) -}) +For Electron main ↔ preload communication, use [simple-electron-ipc](https://github.com/oe/duplex-message/tree/main/packages/simple-electron-ipc#readme). -// listen multi messages by passing a handler map -postMessageHub.on(peerWin, { - // mock a download - download (msg) { - return new Promise((resolve, reject) => { - let progress = 0 - const tid = setInterval(() => { - if (progress >= 100) { - clearInterval(tid) - return resolve('done') - } - // send progress if msg has onprogress property - msg.onprogress && msg.onprogress({progress: progress += 10}) - }, 200) - }) - }, - method2() {} -}) - -// ---- send message to peer --- -// send a message to peerWin, and get the response by `.then` -postMessageHub.emit(peerWin, "fib", 10).then(resp => { - console.log("fibonacci of 10 is", resp) -}) +## Install -// sending a message not handled by the peer will catch an error -postMessageHub.emit(peerWin, "some-not-existing-method").then(resp => { - console.log('response', resp) // this won't run -}).catch(err => { - console.warn('error', err) // bang! -}) +```sh +pnpm add duplex-message +# or: npm install duplex-message ``` -**in iframe window** +## Quick start: call a Worker -```js -import { PostMessageHub } from "duplex-message" +With Vite or another bundler that supports module Workers: -const postMessageHub = new PostMessageHub() -const peerWin = window.parent +**worker.ts** +```ts +import { PostMessageHub, READY_METHOD } from 'duplex-message' -// send a message to the parent and get its response -postMessageHub.emit(peerWin, "pageTitle") - .then(title => { - console.log("page title of main thread", title) - }) - .catch(err => console.error(err)) - -// create a dedicated message hub, so you won't need to pass `peerWin` every time -const dedicatedMessageHub = postMessageHub.createDedicatedMessageHub(peerWin) - -// send message to the parent, don't need the response -dedicatedMessageHub.emit("add", 1, 3).then(res => console.log(res)) - -dedicatedMessageHub.emit("download", { - // pass onprogress so it can receive progress updates - onprogress: (p) => console.log('progress', p) -}).then(res => console.log(res)) +const hub = new PostMessageHub() +hub.on(self, 'add', (a: number, b: number) => a + b) +// Register ready after application methods and asynchronous initialization. +hub.on(self, READY_METHOD, () => true) +``` +**main.ts** -// calc fibonacci, respond by a return -dedicatedMessageHub.on("fib", async (num) => { - // emit a message and wait its response - const title = await dedicatedMessageHub.emit("pageTitle") - console.log(title) - return fib(num) -}); +```ts +import { PostMessageHub, waitForPeer } from 'duplex-message' +const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' }) +const hub = new PostMessageHub({ requestTimeout: 5000 }) -// use a recursive algorithm which will take more than half a minute when n big than 50 -function fib(n) { - if (n < 2) return n - return fib(n - 1) + fib(n - 2) +try { + await waitForPeer(method => hub.emit(worker, method)) + console.log(await hub.emit(worker, 'add', 2, 3)) // 5 +} finally { + hub.destroy() + worker.terminate() } ``` -For more examples, check the [demo folder](./demo/). - -## Usage - -This utility has 4 classes for different scenarios: +`emit` returns a promise. A handler returns its result or a promise; errors reject the caller's promise. Always handle rejection and destroy hubs when their owning view or task ends. -* `PostMessageHub` for windows/iframes/workers communication via `postMessage` -* `StorageMessageHub`for same-origin pages messaging via localStorage's `storage` event -* `PageScriptMessageHub` for isolated JS environments (e.g., UserScripts) in the same window context via `customEvent`, or it can be used as an event bus in the same window context -* `BroadcastMessageHub` for broadcast messaging in same-origin pages (documents) or different Node.js threads (workers) via `BroadcastChannel` +## Typed methods -They all implement the same class `AbstractHub`, so they have a similar API and can be used in the same way. - -### Import & Debug -This utility has a debug mode: -* When the environment variable `process.env.NODE_ENV` is set to any value other than `production`, like `development`, it will log some debug info to the console. -* When the environment variable `process.env.NODE_ENV` is set to `production`, there will be no debug logs. With a bundler tool like `webpack` or `vite`, the output will be optimized and the debug code will be stripped out. +Share an interface between caller and handler. `createRpcClient` checks method names and arguments and infers the awaited result. It uses the existing wire protocol; it does not validate runtime payloads. ```ts -// Use ES6 import -import { PostMessageHub, StorageMessageHub, PageScriptMessageHub, BroadcastMessageHub } from "duplex-message" - -// Or use CommonJS require -const { PostMessageHub, StorageMessageHub, PageScriptMessageHub, BroadcastMessageHub } = require("duplex-message") +import { createRpcClient } from 'duplex-message' -// If you don't want to use the debug mode, or have trouble with the environment variable, you can use a production version +interface WorkerApi { + add(a: number, b: number): number + title(): Promise +} -// Use ES6 import -import { PostMessageHub, StorageMessageHub, PageScriptMessageHub, BroadcastMessageHub } from "duplex-message/dist/duplex-message.production.es" +const rpc = createRpcClient((method, ...args) => hub.emit(worker, method, ...args)) +const sum = await rpc.call('add', 2, 3) // number +const title = await rpc.call({ methodName: 'title', requestTimeout: 1000 }) // string -// Or use CommonJS require -const { PostMessageHub, StorageMessageHub, PageScriptMessageHub, BroadcastMessageHub } = require("duplex-message/dist/duplex-message.production.umd") +// rpc.call('missing') // TypeScript error: unknown method +// rpc.call('add', '2', 3) // TypeScript error: incorrect argument ``` -### constructor -All classes have a constructor with an optional `options` object, you can set some options when creating an instance - ->[!WARNING] ->instances from different classes can't communicate with each other, they are isolated - -```ts -// all classes have the following options -// different classes may its own options -interface IAbstractHubOptions { - /** - * custom instance id, used for identifying different instances - * if not set, a random id will be generated - */ - instanceID?: string | null - /** - * timeout(milliseconds) for waiting heartbeat message, default 500ms - * 1. A heartbeat message will be sent to peer immediately when a request message is received - * and there is at least one handler for it. Or the `emit` method will catch a no handler error - * It has nothing to do with the time of handler execution, there is no timeout - * for handler execution - * 2. Normally, a heartbeat message will be sent to peer in less then 10 ms, - * but you may still need to set a longer timeout if browser is heavy loaded - * and the native apis are slow - */ - heartbeatTimeout?: number -} - -// new an instance with options -const postMessageHub = new PostMessageHub({ instanceID: 'some-id', heartbeatTimeout: 1000 }) -// or use default options -const pageScriptMessageHub = new PageScriptMessageHub() +On the handler side, use a typed object, for example `hub.on(self, { add: (a, b) => a + b } satisfies Pick)`. The original `emit` API remains available; its generic specifies the result without checking method names or arguments. -// StorageMessageHub has its own options keyPrefix -interface IStorageMessageHubOptions extends IAbstractHubOptions { - /** localStorage key prefix to store message, default: $$xiu */ - keyPrefix?: string -} +## Progress, cancellation and deadlines -// new an instance with keyPrefix and instanceID -// different instances must have same keyPrefix to communicate -const storageMessageHub = new StorageMessageHub({ instanceID: 'some-other-id', keyPrefix: 'my-key-prefix' }) -// const storageMessageHub = new StorageMessageHub() // use default options also works +Pass an `onprogress` callback in the **first argument**. The library keeps that callback local and installs a corresponding callback in the peer's first argument. -// BroadcastMessageHub has its own options channelName -interface IBroadcastMessageHubOptions extends IAbstractHubOptions { - /** custom broadcast channel name, default: message-hub */ - channelName?: string -} +**Worker handler:** -// new an instance with channelName and instanceID -// different instances must have same channelName to communicate -const broadcastMessageHub = new BroadcastMessageHub({ instanceID: 'some-other-id', channelName: 'my-channel-name' }) -// const broadcastMessageHub = new BroadcastMessageHub() // use default options also works +```ts +hub.on(self, 'export', async (options: { onprogress: (percent: number) => void }) => { + for (let percent = 10; percent <= 100; percent += 10) { + await new Promise(resolve => setTimeout(resolve, 50)) + options.onprogress(percent) + } + return 'export complete' +}) ``` -### Shared instance -Each class has a shared instance, you can use it directly without new an instance. +**Caller:** ```ts -const postMessageHub = PostMessageHub.shared -const storageMessageHub = StorageMessageHub.shared -const pageScriptMessageHub = PageScriptMessageHub.shared -const broadcastMessageHub = BroadcastMessageHub.shared +import { EErrorCode, type IError } from 'duplex-message' + +const controller = new AbortController() +const result = hub.emit(worker, { + methodName: 'export', + requestTimeout: 10_000, + signal: controller.signal, +}, { onprogress: (percent: number) => console.log(percent) }) + +result.catch((error: IError) => { + if (error.code === EErrorCode.REQUEST_ABORTED) console.log('Cancelled local waiting') + else if (error.code === EErrorCode.REQUEST_TIMEOUT) console.log('Timed out') + else console.error(error) +}) + +// For example, in a Cancel button handler: +// controller.abort() ``` -You will always get the same instance when you use `shared` property in the same context, it's a singleton. +Cancellation and deadlines end **local waiting**. They clear request timers/listeners and ignore late progress/results. An already running peer handler continues; its effects are not undone. An already aborted signal prevents sending. Signals and timeout controls never cross the transport. +`requestTimeout` measures the whole request, including heartbeat wait. Progress does not reset it. Its default is `0` (unlimited); a per-call value overrides the constructor default, and `0` disables that default. Valid values are finite numbers from `0` to `2147483647` milliseconds. -### on -Listen messages sent from peer then respond it by return a value in handler, it has following forms: +New peers explicitly distinguish heartbeat and business progress. When either peer uses an older release, avoid the reserved progress string `--message-hub-to-be-continued--` because the old protocol treats it as a heartbeat. -```ts -// listen one message from peer, and respond it by return in handler -instance.on(peer: any, methodName: string, handler: Function) -// listen multi messages from peer by passing a handler map, and respond it by return in handler. -// * multi handlers are supported for one message -instance.on(peer: any, handlerMap: Record) -// listen all messages from peer with one handler, and respond it by return in handler, the first arg of the generalHandler is the methodName -instance.on(peer: any, generalHandler: Function) -``` +## Iframes and origin restrictions -The parameter `peer` may be ignored in some instances, e.g. `PageScriptMessageHub`, because it's isolated in the same window context. +Use a trusted origin for both outgoing messages and incoming messages. -```ts -// PostMessageHub requires a `peer` to listen messages from, it could be a `Window`, `Worker`, or '*'(any window/worker) -postMessageHub.on(peerWindow, { - hi (name) { - console.log(`name ${name}`) - // response by return - return `hi ${name}` - }, - 'some-method': function (a, b) { - ... - } -}) -// listen a message from a worker -postMessageHub.on(workerInstance, 'some-other-method', console.log) -// listen message from a iframe -postMessageHub.on(someIframe.contentWindow, 'some-iframe-method', console.log) -// in worker context itself, just set peer to self to listen messages from outside -postMessageHub.on(self, 'some-worker-method', console.log) -// listen all messages from all workers/windows. if the methodName has been listened by a specific peer, the handlers from * will be ignored -postMessageHub.on('*', 'some-common-method', console.log) -// listen all message from win in one handler -postMessageHub.on(win, async function (methodName, ...args) { - ... -}) +**Parent:** -// ------------------- +```ts +import { PostMessageHub, waitForPeer } from 'duplex-message' -// StorageMessageHub doesn't require a `peer` to listen messages from, it's isolated in the same window context -storageMessageHub.on('async-add', async function (a, b) { - return new Promise((resolve, reject) => { - resolve(a + b) - }) +const iframe = document.querySelector('#editor')! +const childOrigin = new URL(iframe.src).origin +const hub = new PostMessageHub({ + allowedOrigins: [childOrigin], + targetOrigin: childOrigin, }) -// ------------------- - -// PageScriptMessageHub doesn't require a `peer` to listen messages from, it's isolated in the same window context -pageScriptMessageHub.on('async-add', async function (a, b) { - return new Promise((resolve, reject) => { - resolve(a + b) - }) -}) +await waitForPeer(method => hub.emit(iframe.contentWindow!, method)) +const result = await hub.emit(iframe.contentWindow!, 'add', 2, 3) +``` -// ------------------- +**Iframe:** -// BroadcastMessageHub doesn't require a `peer` to listen messages from, it's required to set the same channelName to communicate(and same-origin in browser) -broadcastMessageHub.on('async-add', async function (a, b) { - return new Promise((resolve, reject) => { - resolve(a + b) - }) -}) +```ts +import { PostMessageHub, READY_METHOD } from 'duplex-message' +const parentOrigin = 'https://app.example.com' // Your trusted application origin. +const hub = new PostMessageHub({ allowedOrigins: [parentOrigin], targetOrigin: parentOrigin }) +hub.on(parent, 'add', (a: number, b: number) => a + b) +hub.on(parent, READY_METHOD, () => true) ``` +`allowedOrigins` filters incoming Window messages before handlers and response callbacks run. Replies to accepted window messages use the verified sender origin. `targetOrigin` is the default for outgoing window messages; a per-call `targetOrigin` overrides it. ->[!WARNING] ->Although a message can be listened by multiple handlers, only one response from a handler will be sent to the peer. It follows the first-come-first-serve rule: -> 1. All handlers will be called when a message is received at the same time. -> 2. The first handler that successfully returns a non-undefined value will be the response; others will be ignored. -> 3. If no handler returns a non-undefined value, then the response will be `undefined`. -> 4. Errors thrown by handlers will be ignored if there is a successful call. -> 5. If all handlers throw an error, the last error will be caught and sent to the peer. - +For compatibility, omitting `allowedOrigins` accepts any origin and the default `targetOrigin` is `'*'`. Configure explicit origins for trusted iframe/window integrations. An empty list rejects all window origins; `['*']` explicitly accepts all. Origin checks apply to Window messages; Worker messages have no meaningful Window origin. Validate application arguments and permissions in handlers as well. -### emit -Emit a message to a peer, invoking `methodName` registered on the peer via [`on`](#on) with all its arguments `args`, and return a promise with the response from the peer. +For multiple trusted origins, set a per-call `targetOrigin` whenever the destination differs from the hub's configured default. For opaque origins such as sandboxed iframes, the string `'null'` does not uniquely identify a trusted site; prefer an origin-preserving configuration. -```ts +## Wait for application readiness -interface IMethodNameConfig { - methodName: string - /** peer instance ID that can receive the message */ - to?: string -} +Register `READY_METHOD` **after** initialization and handler registration: -// In TypeScript, use ResponseType to specify the response type -// Send a message and get a response -instance.emit(peer: any, methodName: string | IMethodNameConfig, ...args: any[]) => Promise +```ts +await initializeApplication() +hub.on(peer, 'export', exportDocument) +hub.on(peer, READY_METHOD, () => true) ``` -Just like [`on`](#on), the parameter `peer` may be ignored in some instances, e.g., `PageScriptMessageHub`, because it's isolated in the same window context. +The caller uses: ```ts -// PostMessageHub requires a `peer` to send messages to; it could be a `Window` or `Worker`. (`*` is not allowed when emitting messages) -postMessageHub.emit(peerWindow, 'some-method', 'arg1', 'arg2', 'otherArgs').then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) -// Send a message to a worker that has instance ID 'custom-instanceID'. If the instance ID does not match, an error will be thrown -postMessageHub.emit(workerInstance, { methodName: 'some-method', instanceID: 'custom-instanceID'}, 'arg1', 'arg2', 'otherArgs').then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) +await waitForPeer(method => hub.emit(peer, method), { + timeout: 5000, + interval: 100, + signal: controller.signal, }) +``` -// in worker context itself, just set peer to self to send messages to outside -postMessageHub.emit(self, 'another-method', 'arg1', 'arg2', 'otherArgs').then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) +`waitForPeer` retries only the explicit ready endpoint, never a business operation. A ready handler returns `true`; it may return `false` while still initializing. Missing handlers and probe timeouts are retried. Other handler errors reject immediately. The overall deadline also works when an adapter fails to resolve. Completion, cancellation and timeout release the wait's timers/listeners. -// ------------------- +The peer must implement the ready endpoint, including when communicating with older releases. A catch-all handler should route `READY_METHOD` explicitly. The defaults are a 5000ms total deadline and a 100ms probe timeout/pause; both options must be positive finite numbers up to `2147483647`. -// StorageMessageHub doesn't require a `peer` to send messages to, it's isolated in the same window context -storageMessageHub.emit('async-add', 23, 12).then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) +## Choose a transport -// ------------------- +| Class | Contexts | Native transport | +| --- | --- | --- | +| `PostMessageHub` | Windows, iframes, dedicated browser Workers | `postMessage` | +| `BroadcastMessageHub` | Same-origin browser tabs, Node BroadcastChannel contexts | `BroadcastChannel` | +| `StorageMessageHub` | Same-origin browser windows/tabs | `localStorage` storage events | +| `PageScriptMessageHub` | Separate hub instances in the same page; page/content-script bridges | `CustomEvent` | -// PageScriptMessageHub doesn't require a `peer` to send messages to, it's isolated in the same window context -pageScriptMessageHub.emit('async-add', 223, 89).then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) +Different transport classes do not communicate with each other. `PageScriptMessageHub` is not a complete browser-extension background/service-worker router. -// ------------------- +The last three classes omit the `peer` argument: -// BroadcastMessageHub doesn't require a `peer` to send messages to, it's required to set the same channelName to communicate(and same-origin in browser) -broadcastMessageHub.emit('async-add', 223, 89).then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) - -``` - ->[!WARNING] -> 1. If there are multiple peers listening to the same message, you'll only get the first one who responds; others will be ignored. However, all handlers from different peers will be called. -> 2. If you want to send a message to a specific peer, you should set the `to` property in the `methodName` object, and the peer must have the same instance ID as the `to` property. -> 3. If the message has no listener, the promise will be rejected with an error `no corresponding handler found for method ${methodName}` -> 4. If the handler throws an error, the promise will be rejected with the error thrown by the handler (error object may be lost in some cases due to serialization issues) -> 5. Please always handle the promise rejection returned by `emit` to avoid unhandled promise warnings +```ts +import { BroadcastMessageHub, createRpcClient } from 'duplex-message' -### off -Remove message handlers. +// Responding tab: +const server = new BroadcastMessageHub({ channelName: 'editor' }) +server.on('title', () => document.title) -```ts -// 1. If handler is provided, remove the specific handler. -// 2. If only methodName is provided, remove all handlers for that methodName. -// 3. If no methodName is provided, remove all handlers for the peer. -instance.off(peer: any, methodName?: string, handler?: Function) +// Calling tab: +const client = new BroadcastMessageHub({ channelName: 'editor' }) +const rpc = createRpcClient<{ title(): string }>((method, ...args) => client.emit(method, ...args)) +console.log(await rpc.call('title')) +// Destroy both hubs when their owning views end. ``` -Just like [`on`](#on), the parameter `peer` may be ignored in some instances, e.g., `PageScriptMessageHub`, because it's isolated in the same window context. +Broadcast requests can execute handlers in multiple peers. The caller accepts one selected responder's result, not an aggregate; this is request/response RPC rather than a fan-out collection API. Specify `to: peer.instanceID` in method configuration to address one hub. +## API reference +### Hub options -```ts -postMessageHub.off(peerWindow, 'some-method', someHandler) -postMessageHub.off(peerWindow, 'some-method') -postMessageHub.off(peerWindow) +All hubs accept `{ instanceID?, heartbeatTimeout?, requestTimeout? }`. -storageMessageHub.off('async-add', someHandler) -storageMessageHub.off('async-add') -storageMessageHub.off() +- `instanceID`: custom peer identity; generated when omitted. +- `heartbeatTimeout`: handler-acknowledgement wait, default 500ms. It does not limit handler duration. If it expires before a total request deadline, the caller receives `METHOD_NOT_FOUND`. +- `requestTimeout`: optional total deadline, default `0`. -pageScriptMessageHub.off('async-add', someHandler) -pageScriptMessageHub.off('async-add') -pageScriptMessageHub.off() +Transport options: -broadcastMessageHub.off('async-add', someHandler) -broadcastMessageHub.off('async-add') -broadcastMessageHub.off() -``` +- `PostMessageHub`: `allowedOrigins?: readonly string[]`, `targetOrigin?: string`. +- `BroadcastMessageHub`: `channelName?: string`. +- `StorageMessageHub`: `keyPrefix?: string`. +- `PageScriptMessageHub`: `customEventName?: string`. -### destroy -Destroy the instance: remove all message handlers and references to objects. +Every class provides a `shared` getter. Destroying a shared hub allows a later getter to create a new instance. -```ts -// You can't use the instance after destroy, it will throw an exception. -instance.destroy() -``` +### on, emit, off and destroy -### progress -Track the progress of a long-running task. +```ts +hub.on(peer, 'add', (a: number, b: number) => a + b) +hub.on(peer, { title: () => document.title, add: (a: number, b: number) => a + b }) +hub.on(peer, (methodName: string, ...args: unknown[]) => route(methodName, args)) -If you need progress feedback when peer handling you requests, you can do it by setting the first argument as an object and has a function property named `onprogress` when `emit` messages, and call `onprogress` in `on` on the peer's side. +await hub.emit(peer, 'add', 2, 3) +await hub.emit(peer, { methodName: 'add', to: peerInstanceID, requestTimeout: 5000 }, 2, 3) -```ts -// emit message with onprogress callback in the first message argument -// * the parameter `peer` is only required in PostMessageHub -instance.emit(peer, methodName: string, { - onprogress: (progressData: any) => void - ... -}, ...args: any[]) - -// listen message and call onprogress if exists in the first message argument -// * the parameter `instance` is only required in PostMessageHub -peer.on(instance, methodName: string, async (msg, ...args) => { - // call onprogress if exists - msg.onprogress && msg.onprogress({progress: 10}) - // update progress - ... - await someAsyncFn() - return 'done' -}) +hub.off(peer, 'add', specificHandler) // Remove one handler. +hub.off(peer, 'add') // Remove handlers for this method. +hub.off(peer) // Remove handlers for this peer. +hub.destroy() // Reject pending calls and release resources. ``` -### proxy for PostMessageHub -PostMessageHub has a `createProxy` method that can forward all messages from `fromWin` to `toWin` then forward `toWin`'s response to the `fromWin`, instead of handle messages by self, it's useful when you want to make two windows communicate directly. - -```ts -// create proxy message from `fromWin` to `toWin` -postMessageHub.createProxy(fromWin: Window | Worker, toWin: Window | Worker) -// stop proxy message from a peer -postMessageHub.stopProxy(fromWin: Window | Worker) +Omit `peer` for Broadcast/Storage/PageScript hubs. `PostMessageHub.on('*', ...)` registers a fallback handler for attached peers. Registering a Worker handler or emitting to it attaches its listener; a wildcard registration alone does not discover Workers. +Handler maps can contain arrays of functions. All applicable functions run; the first defined successful result wins. If all successful handlers return `undefined`, the request resolves with `undefined`. Catch-all handlers receive the method name as their first argument. +`destroy()` is idempotent. Pending calls reject with `UNKNOWN`; peer-side work is not cancelled. Completed/failed/cancelled calls release response state, timers and abort listeners. Worker listeners remain while pending calls or registered handlers need them. -// forward all messages from `someWorker` to `someIframeWin` -postMessageHub.createProxy(someWorker, someIframeWin) -// then forward all messages from `someIframeWin` to `someWorker` -postMessageHub.createProxy(someIframeWin, someWorker) -// with above two lines, `someWorker` and `someIframeWin` can communicate directly, postMessageHub will be a transparent proxy(bridge) between them +### Method configuration and transferables -// stop proxy -postMessageHub.stopProxy(someWorker) -postMessageHub.stopProxy(someIframeWin) +`emit` accepts a method string or `{ methodName, to?, signal?, requestTimeout? }`. PostMessage calls additionally accept `targetOrigin` and `transfer`: +```ts +const buffer = new ArrayBuffer(1024) +await hub.emit(worker, { methodName: 'process', transfer: [buffer] }, buffer) ``` +Arguments/results must be supported by the underlying transport's serialization. Transferred buffers are detached from the sender. Storage uses JSON-compatible data. Avoid exposing arbitrary privileged operations through wildcard or catch-all handlers. -### dedicated instance for PostMessageHub -PostMessageHub has a `createDedicatedMessageHub` method that can create a dedicated message-hub for specified peer, so that you won't need to pass peer every time. +### Dedicated hubs and proxies ```ts - -/** -* @param peer peer window to communicate with, or you can set it later via `setPeer` -* @param silent when peer not exists, keep silent instead of throw an error when call emit, on, off -*/ -const dedicatedMessageHub = postMessageHub.createDedicatedMessageHub (peer?: Window | Worker, silent?: boolean) => IDedicatedMessageHub - -interface IDedicatedMessageHub { - /** if you didn't set a peer when invoking createDedicatedMessageHub, then you can use `setPeer` to set it when it's ready*/ - setPeer: (peer: Window | Worker) => void; - // in typescript, use ResponseType to specify response type - emit: (methodName: string, ...args: any[]) => Promise; - on: (methodName: string, handler: Function) => void; - on: (handlerMap: Record) => void; - off: (methodName?: string) => any; -} - -dedicatedMessageHub.emit('some-method', 'arg1', 'arg2').then(res => { - console.log('success', res) -}).catch(err => { - console.warn('error', err) -}) - -dedicatedMessageHub.on('some-method', console.log) -dedicatedMessageHub.off('some-method') +const dedicated = hub.createDedicatedMessageHub(worker) +await dedicated.emit('add', 2, 3) +dedicated.on('notify', handler) +dedicated.off('notify') +dedicated.setPeer(anotherWorker) + +// Forward iframe requests to a Worker from a normal window context. +hub.createProxy(iframe.contentWindow!, worker) +hub.stopProxy(iframe.contentWindow!) ``` +Pass `silent: true` as the second `createDedicatedMessageHub` argument to suppress missing-peer registration errors; emitting without a peer still rejects with `PEER_NOT_FOUND`. Proxies forward RPC through handlers, so business progress and response results are preserved. -### Error -when you catch an error from `emit`, it conforms the following structure `IError` +### Errors -```ts -/** error object could be caught via emit().catch(err) */ -interface IError { - /** none-zero error code */ - code: EErrorCode - /** error message */ - message: string - /** error object if it could pass through via the message channel underground*/ - error?: Error -} +Rejected calls expose `IError`: `{ code: EErrorCode, message: string, error?: Error }`. Import the enum from `duplex-message`. -/** enum of error code */ -enum EErrorCode { - /** handler on other side encounter an error */ - HANDLER_EXEC_ERROR = 1, - /** peer not found */ - PEER_NOT_FOUND = 2, - /** method not found in peer */ - METHOD_NOT_FOUND = 3, - /** message has invalid content, can't be sent */ - INVALID_MESSAGE = 4, - /** other unspecified error */ - UNKNOWN = 5, -} -``` +| Code | Name | Meaning | +| --- | --- | --- | +| 1 | `HANDLER_EXEC_ERROR` | A peer handler failed. | +| 2 | `PEER_NOT_FOUND` | Peer missing or unloaded. | +| 3 | `METHOD_NOT_FOUND` | No matching handler acknowledgement in time. | +| 4 | `INVALID_MESSAGE` | Invalid controls, serialization or transport failure. | +| 5 | `UNKNOWN` | Unspecified error, including hub destruction. | +| 6 | `REQUEST_ABORTED` | Caller cancelled local waiting. | +| 7 | `REQUEST_TIMEOUT` | Request or readiness deadline expired. | -## FAQ -### How to deal with the error `no corresponding handler found for method xxx`? -This may happen in the following situations: -1. the message was sent to the peer, but the peer has no handler for it. -2. the message was sent to the wrong peer, or there is no peer(or the peer has not been set / loaded correctly). - 1. the peer is not using the same class that current instance using - 2. the peer has not been loaded(e.g.: iframe not loaded, worker not started) - 3. the peer is using different channel(e.g.: none-match `keyPrefix` for `StorageMessageHub`, none-match `channelName` for `BroadcastMessageHub`) -3. The message was sent to the wrong peer: `emit` specifies a `to` property in the `methodName` object, but the peer does not have the same instance ID as the `to` property. -4. the runtime(browser/node) is heavy loaded, the native api is too slow, you may need to set [a longer `heartbeatTimeout` for the instance](#constructor) +### Imports and debug output -## Development +The package provides ESM, CommonJS and type declarations for TypeScript 4.1 or later. The workspace's TypeScript 7 compiler is not required by consumers. Use normal package imports with a bundler or Node. Legacy `dist/index.es.js`, UMD and production filenames remain available, including extensionless distribution imports; explicit ESM filenames end in `.mjs`. The UMD global is `duplex-message`. Published JavaScript targets ES2018 and is checked against that syntax level. The selected transport must be supported by the runtime; readiness and cancellation additionally need `AbortController`/`AbortSignal`. -```sh -# install dependencies, exec in root of the repo -pnpm install -# dev -pnpm dev +When upgrading from 2.1, upgrade TypeScript if using a compiler older than 4.1. `destroy()` now rejects pending calls with `UNKNOWN` instead of leaving them unresolved; handle rejections from each call. The existing `emit` signatures and wire protocol are retained. `signal` and `requestTimeout` in method configuration are reserved local controls, and the legacy `heartbeatTimeout: 0` fallback remains 500ms. -#!! make sure install chromium before running playwright tests by following command -pnpm playwright install chromium +Debug logging is enabled when `process.env.NODE_ENV` exists and is not `production`. Bundlers can replace that value; production-specific browser builds are also available at `duplex-message/dist/index.production.es.mjs` and `duplex-message/dist/index.production.umd.js`. -# test -pnpm test +## When to choose another approach -# build -pnpm build +| Need | Consider | +| --- | --- | +| Built-in transports, familiar event names and per-request progress | duplex-message | +| Remote Worker objects/properties and proxy callbacks | [Comlink](https://github.com/GoogleChromeLabs/comlink) | +| Established window/iframe connections and MessagePort integrations | [Penpal](https://github.com/Aaronius/penpal) | +| Typed RPC over a custom transport such as WebSocket | [birpc](https://github.com/antfu-collective/birpc) | +| Only a handful of simple messages | Native `postMessage` / `MessageChannel` may be sufficient | -# publish -pnpm publish -r -``` +See the [runnable browser demos](https://github.com/oe/duplex-message/tree/main/packages/duplex-message/demo) and [workspace development instructions](https://github.com/oe/duplex-message#development). CI exercises Node, Chromium/Firefox/WebKit Workers and iframes, and packed CJS/ESM consumers. This covers current Playwright engines; older browsers, mobile Safari and embedded WebViews still require application-level verification. ## License -MIT + +MIT. See [LICENSE](https://github.com/oe/duplex-message/blob/main/LICENSE). diff --git a/packages/duplex-message/build.config.ts b/packages/duplex-message/build.config.ts deleted file mode 100644 index 8d6750c..0000000 --- a/packages/duplex-message/build.config.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { build, UserConfig } from 'vite' -import dts from 'vite-plugin-dts' - -const defaultConfig: UserConfig = { - plugins: [dts({ - rollupTypes: true, - exclude: ['test/**', 'demo/**'], - })], - build: { - lib: { - entry: 'src/index.ts', - name: 'duplex-message', - formats: ['umd', 'es'], - fileName: (format) => (format === 'umd' ? 'index.umd.js' : 'index.es.js'), - }, - }, -} - -const productionConfig: UserConfig = { - build: { - emptyOutDir: false, - // @ts-ignore - lib: { - ...defaultConfig.build!.lib, - fileName: (format) => (format === 'umd' ? 'index.production.umd.js' : 'index.production.es.js') - } - }, - define: { - 'process.env.NODE_ENV': '"production"', - } -} - -async function main() { - await build(defaultConfig) - await build(productionConfig) -} - -main() diff --git a/packages/duplex-message/demo/frame/frame.ts b/packages/duplex-message/demo/frame/frame.ts index 3ed54e0..1738845 100644 --- a/packages/duplex-message/demo/frame/frame.ts +++ b/packages/duplex-message/demo/frame/frame.ts @@ -2,13 +2,13 @@ import { PostMessageHub, setConfig } from '../../src' const postMessageHub = new PostMessageHub import PeerWorker from './worker?worker' -setConfig({debug: true}) +setConfig() const peer = new PeerWorker const messageHub = postMessageHub.createDedicatedMessageHub(parent) const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement + return document.getElementById(id.replace(/^#/, '')) as HTMLElement } const subFrameWin = (document.getElementById('sub-iframe') as HTMLFrameElement).contentWindow diff --git a/packages/duplex-message/demo/frame/index.ts b/packages/duplex-message/demo/frame/index.ts index 38d7676..388efb0 100644 --- a/packages/duplex-message/demo/frame/index.ts +++ b/packages/duplex-message/demo/frame/index.ts @@ -1,6 +1,6 @@ import { PostMessageHub, setConfig } from '../../src' -setConfig({debug: true}) +setConfig() const postMessageHub = PostMessageHub.shared // @ts-ignore @@ -12,7 +12,7 @@ const messageHub = postMessageHub.createDedicatedMessageHub(frameWin!) // @ts-ignore window.mh = messageHub const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement + return document.getElementById(id.replace(/^#/, '')) as HTMLElement } postMessageHub.on('*', "page-title", (arg: any) => { diff --git a/packages/duplex-message/demo/frame/sub-frame.ts b/packages/duplex-message/demo/frame/sub-frame.ts index a1b616c..453f3ef 100644 --- a/packages/duplex-message/demo/frame/sub-frame.ts +++ b/packages/duplex-message/demo/frame/sub-frame.ts @@ -3,7 +3,7 @@ const postMessageHub = new PostMessageHub const messageHub = postMessageHub.createDedicatedMessageHub(parent) const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement + return document.getElementById(id.replace(/^#/, '')) as HTMLElement } $('#test-1').addEventListener('click', () => { diff --git a/packages/duplex-message/demo/page/index.ts b/packages/duplex-message/demo/page/index.ts index 6dedf20..132707c 100644 --- a/packages/duplex-message/demo/page/index.ts +++ b/packages/duplex-message/demo/page/index.ts @@ -1,5 +1,5 @@ import { PageScriptMessageHub, setConfig } from "../../src"; -setConfig({ debug: true }) +setConfig() const port2 = new PageScriptMessageHub({ instanceID: 'port2' }) const port3 = new PageScriptMessageHub({ instanceID: 'port3' }) @@ -15,7 +15,7 @@ console.warn('port1', port1.instanceID) console.warn('port3', port3.instanceID) const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement; + return document.getElementById(id.replace(/^#/, '')) as HTMLElement; } port1.on({ diff --git a/packages/duplex-message/demo/storage/index.ts b/packages/duplex-message/demo/storage/index.ts index f860593..ddeffd1 100644 --- a/packages/duplex-message/demo/storage/index.ts +++ b/packages/duplex-message/demo/storage/index.ts @@ -1,12 +1,12 @@ import { StorageMessageHub, setConfig } from '../../src' -setConfig({ debug: true }) +setConfig() const query = new URLSearchParams(location.search) const storageMessageHub = new StorageMessageHub({ instanceID: query.get('name') }) // @ts-ignore window.sm = storageMessageHub const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement + return document.getElementById(id.replace(/^#/, '')) as HTMLElement } diff --git a/packages/duplex-message/package.json b/packages/duplex-message/package.json index 43bed9b..f46aa3f 100644 --- a/packages/duplex-message/package.json +++ b/packages/duplex-message/package.json @@ -1,48 +1,67 @@ { "name": "duplex-message", "version": "2.1.0", - "description": "makes one way message responsive, enhance postMessage/storageEvent/chrome extension scripts", + "description": "Promise-based RPC for iframes, Web Workers and browser tabs, with progress feedback, typed calls and local cancellation.", "main": "dist/index.umd.js", - "module": "dist/index.es.js", - "typings": "dist/index.d.ts", + "module": "dist/index.es.mjs", "scripts": { - "dev": "vite", - "build": "tsx build.config.ts", - "prepublish": "pnpm build", - "test": "vitest", - "test:coverage": "vitest --coverage" - }, - "husky": { - "hooks": { - "pre-commit": "pnpm lint" - } + "build": "vite build && vite build --mode production-lib && tsc -p tsconfig.build.json", + "typecheck": "tsc --noEmit", + "prepublishOnly": "pnpm build", + "test": "vitest run", + "dev": "vite --mode demo", + "build:demo": "vite build --mode demo", + "test:coverage": "vitest run --coverage" }, "files": [ "dist/*" ], "devDependencies": { - "@vitest/browser": "^2.0.2", - "@vitest/coverage-istanbul": "^2.0.2", - "playwright": "^1.45.1", - "tsx": "^4.17.0", "typescript": "catalog:", "vite": "catalog:", - "vite-plugin-dts": "catalog:", - "vitest": "catalog:" + "vitest": "catalog:", + "@vitest/browser-playwright": "catalog:", + "@vitest/coverage-v8": "catalog:", + "playwright": "catalog:" }, "keywords": [ "rpc", - "duplex", - "duplex-message", - "hub", "postmessage", - "promise", "iframe", + "worker", "webworker", - "worker" + "web-worker", + "cross-window", + "cross-origin", + "broadcastchannel", + "browser-tabs", + "progress", + "typescript", + "promise" ], - "repository": "https://oe@github.com/oe/duplex-message.git", + "repository": { + "type": "git", + "url": "https://github.com/oe/duplex-message.git", + "directory": "packages/duplex-message" + }, "author": "Saiya", "license": "MIT", - "homepage": "https://github.com/oe/duplex-message/tree/master/packages/duplex-message" + "homepage": "https://github.com/oe/duplex-message/tree/main/packages/duplex-message", + "type": "commonjs", + "sideEffects": false, + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": "./dist/index.cjs", + "import": "./dist/index.es.mjs", + "require": "./dist/index.cjs" + }, + "./dist/*": "./dist/*", + "./package.json": "./package.json", + "./dist/index.umd": "./dist/index.umd.js", + "./dist/index.es": "./dist/index.es.js", + "./dist/index.production.umd": "./dist/index.production.umd.js", + "./dist/index.production.es": "./dist/index.production.es.js" + } } diff --git a/packages/duplex-message/src/abstract.ts b/packages/duplex-message/src/abstract.ts index 7b13dbb..28c1a63 100644 --- a/packages/duplex-message/src/abstract.ts +++ b/packages/duplex-message/src/abstract.ts @@ -1,3 +1,6 @@ +// Response/progress events have no asynchronous work to await. +const COMPLETED = Promise.resolve() + export type IFn = (...args: any[]) => any export type IHandlerMap = Record @@ -70,6 +73,8 @@ export interface IProgress extends IMessageBase { to: string type: 'progress' data: T + /** Explicitly separates heartbeat control messages from business progress. */ + heartbeat?: boolean } /** enum of error code */ @@ -84,6 +89,10 @@ export const enum EErrorCode { INVALID_MESSAGE = 4, /** other unspecified error */ UNKNOWN = 5, + /** the caller aborted the request */ + REQUEST_ABORTED = 6, + /** the request exceeded its configured deadline */ + REQUEST_TIMEOUT = 7, } /** error object could be caught via emit().catch(err) */ @@ -100,6 +109,10 @@ export interface IMethodNameConfig { methodName: string /** peer instance id */ to?: string + /** Abort waiting locally; does not cancel the peer's handler. */ + signal?: AbortSignal + /** Total request deadline in milliseconds; 0 disables it. Overrides the hub default. */ + requestTimeout?: number [k: string]: any } @@ -129,15 +142,24 @@ export interface IAbstractHubOptions { instanceID?: string | null /** * timeout(milliseconds) for waiting heartbeat message, default 500ms - * 1. A heartbeat message will be sent to peer immediately when a request message is received + * 1. A heartbeat message will be sent to peer immediately when a request message is received * and there is at least one handler for it. Or the `emit` method will catch a no handler error - * It has nothing to do with the time of handler execution, there is no timeout - * for handler execution + * This only bounds heartbeat wait. Use requestTimeout to bound the total request. * 2. Normally, a heartbeat message will be sent to peer in less then 10 ms, * but you may still need to set a longer timeout if browser is heavy loaded * and the native apis are slow */ heartbeatTimeout?: number + /** Total request deadline in milliseconds, including heartbeat wait. Default 0 (disabled). */ + requestTimeout?: number +} + +interface IPendingRequest { + peer: any + reject: (error: IError) => void + heartbeatTimer?: ReturnType + requestTimer?: ReturnType + abortCleanup?: () => void } export abstract class AbstractHub { @@ -168,21 +190,31 @@ export abstract class AbstractHub { */ protected _heartbeatTimeout: number + protected readonly _requestTimeout: number + /** * inner props to store whether instance is destroyed */ protected isDestroyed: boolean + private readonly _pendingRequests = new Map() + + private readonly _pendingPeerCounts = new Map() + /** * init Hub, subclass should implement its own constructor */ constructor(options?: IAbstractHubOptions) { this.instanceID = (options && options.instanceID) || AbstractHub.generateInstanceID() this._eventHandlerMap = [] - this._responseCallbackMap = {} + this._responseCallbackMap = Object.create(null) this._messageID = 0 - this._designedResponse = {} - this._heartbeatTimeout = (options && options.heartbeatTimeout) || DEFAULT_HEARTBEAT_WAIT_TIMEOUT + this._designedResponse = Object.create(null) + this._heartbeatTimeout = options?.heartbeatTimeout || DEFAULT_HEARTBEAT_WAIT_TIMEOUT + this._requestTimeout = options?.requestTimeout ?? 0 + if (!AbstractHub.isValidRequestTimeout(this._requestTimeout)) { + throw new RangeError('requestTimeout must be a finite number between 0 and 2147483647') + } this.isDestroyed = false if (ENABLE_DEBUG) { console.log(`[duplex-message] create instance of ${this.constructor.name}, instanceID: ${this.instanceID}`) @@ -298,62 +330,131 @@ export abstract class AbstractHub { /** destroy instance */ destroy() { - this._eventHandlerMap.length = 0 - this._responseCallbackMap = {} - this._designedResponse = {} + if (this.isDestroyed) return this.isDestroyed = true + for (const [messageID, pending] of this._pendingRequests) { + pending.reject({ code: EErrorCode.UNKNOWN, message: 'instance has been destroyed' }) + this.clearPendingRequest(messageID) + } + this._eventHandlerMap.length = 0 + this._responseCallbackMap = Object.create(null) + this._designedResponse = Object.create(null) } /** * listen message from peer */ - protected async onMessage(peer: any, msg: any) { - this.checkInstance() - if (!this.isMessage(msg)) return - // then it is a response or progress message - if (!this.isRequestMessage(msg)) { - // @ts-expect-error ignore - this.runResponseCallback(msg) - return - } - // then it is a request message from a peer + protected onMessage(peer: any, msg: any): Promise { + try { + if (this.isDestroyed || !msg || typeof msg !== 'object') return COMPLETED + // then it is a response or progress message + if (!this.isRequestMessage(msg)) { + this.runResponseCallback(msg as IResponse | IProgress, peer) + return COMPLETED + } + // then it is a request message from a peer - // check if there is a handler for the request message - const callbackInfo = this.getMessageCallbacks(peer, msg) + // check if there is a handler for the request message + const callbackInfo = this.getMessageCallbacks(peer, msg) - if (!callbackInfo) { - return + if (!callbackInfo) { + return COMPLETED + } + return this.respondToRequest(peer, msg, callbackInfo) + } catch (error) { + // Preserve the Promise-based contract for throwing subclass hooks/getters. + return Promise.reject(error) } - // send a heartbeat message to peer, in case of response takes too long - this.sendMessage( - peer, - this.buildProgressMessage(CONTINUE_INDICATOR, msg), - ) + } - const response = await this.runMessageCallbacks(peer, callbackInfo, msg) - this.sendMessage(peer, response) + private async respondToRequest( + peer: any, msg: IRequest, + callbackInfo: NonNullable>, + ) { + // send a heartbeat message to peer, in case of response takes too long + try { + this.sendMessage(peer, this.buildProgressMessage(CONTINUE_INDICATOR, msg, true)) + const response = await this.runMessageCallbacks(peer, callbackInfo, msg) + if (this.isDestroyed) return + try { + this.sendMessage(peer, response) + } catch { + this.sendMessage(peer, this._buildRespMessage({ + code: EErrorCode.INVALID_MESSAGE, + message: 'unable to send response', + }, msg, false)) + } + } catch (error) { + // Native event listeners cannot consume a rejected async callback. + if (ENABLE_DEBUG) console.warn('[duplex-message] unable to respond to message', error) + } } - protected runResponseCallback(resp: IResponse | IProgress) { + protected runResponseCallback(resp: IResponse | IProgress, peer?: any) { + if (peer !== undefined && this._pendingRequests.get(resp.messageID)?.peer !== peer) return false const callback = this._responseCallbackMap[resp.messageID] if (!callback) return false const ret = callback(resp) // not match if (!ret) return false // need to be continued - if (ret > 1) return true + if (ret > 1) { + this.clearHeartbeatTimer(resp.messageID) + return true + } // done // clean up - delete this._responseCallbackMap[resp.messageID] - delete this._designedResponse[resp.messageID] + this.clearPendingRequest(resp.messageID) return true } + private clearHeartbeatTimer(messageID: number) { + const pending = this._pendingRequests.get(messageID) + if (pending?.heartbeatTimer !== undefined) { + clearTimeout(pending.heartbeatTimer) + pending.heartbeatTimer = undefined + } + } + + private clearPendingRequest(messageID: number) { + const pending = this._pendingRequests.get(messageID) + if (!pending) return + // Remove state before invoking cleanup hooks so reentrant settlement is harmless. + this._pendingRequests.delete(messageID) + const { peer, heartbeatTimer, requestTimer, abortCleanup } = pending + const count = this._pendingPeerCounts.get(peer)! - 1 + if (count) this._pendingPeerCounts.set(peer, count) + else this._pendingPeerCounts.delete(peer) + if (heartbeatTimer !== undefined) clearTimeout(heartbeatTimer) + if (requestTimer !== undefined) clearTimeout(requestTimer) + delete this._responseCallbackMap[messageID] + delete this._designedResponse[messageID] + abortCleanup?.() + this.onRequestSettled(peer) + } + + private rejectPendingRequest(messageID: number, error: IError) { + const pending = this._pendingRequests.get(messageID) + if (!pending) return + pending.reject(error) + this.clearPendingRequest(messageID) + } + + protected hasPendingRequests(peer: any) { + return this._pendingPeerCounts.has(peer) + } + + /** Allow transports to release listeners once a peer has no pending calls. */ + protected onRequestSettled(_peer: any): void {} + + /** Attach transport listeners only when a request is ready to be sent. */ + protected onRequestStarted(_peer: any): void {} + /** * run message's callbacks when receive request from peer * * first none undefined response will be returned * * if all callbacks occur error, the last error will be returned - * * if at least one callback success, and no none undefined response, + * * if at least one callback success, and no none undefined response, * success response(undefined) will be returned */ protected runMessageCallbacks( @@ -363,11 +464,12 @@ export abstract class AbstractHub { ) { const { methodName, data } = reqMsg const [method, isGeneral] = callbackInfo - const newArgs = data.slice(0) + let newArgs = data if (reqMsg.progress && newArgs[0]) { + newArgs = data.slice() const newArg = { ...newArgs[0] } newArg.onprogress = (d: any) => { - this.sendMessage(peer, this.buildProgressMessage(d, reqMsg)) + if (!this.isDestroyed) this.sendMessage(peer, this.buildProgressMessage(d, reqMsg)) } newArgs[0] = newArg } @@ -375,11 +477,29 @@ export abstract class AbstractHub { // add methodName as the first argument if handlerMap is a function if (isGeneral) { - newArgs.unshift(methodName) + newArgs = [methodName, ...newArgs] methods = [method] } else { methods = method } + + // Most RPC endpoints have one handler. Avoid the multi-handler race machinery here. + if (methods.length === 1 && typeof methods[0] === 'function') { + try { + const result = methods[0](...newArgs) + if (result === null || (typeof result !== 'object' && typeof result !== 'function')) { + return Promise.resolve(this._buildRespMessage(result, reqMsg, true)) + } + return Promise.resolve(result).then( + value => this._buildRespMessage(value, reqMsg, true), + error => this._buildRespMessage(this.handlerError(error, method, newArgs), reqMsg, false), + ) + } catch (error) { + return Promise.resolve(this._buildRespMessage(this.handlerError(error, method, newArgs), reqMsg, false)) + } + } + // Snapshot before invoking handlers: on/off during an invocation must not alter the race. + methods = methods.slice() let responded = false let lastError: IError | undefined let hasSuccess = false @@ -399,16 +519,8 @@ export abstract class AbstractHub { } hasSuccess = true } catch (error) { - if (ENABLE_DEBUG) { - console.warn('[duplex-message] run handler error', method, 'with arguments', newArgs, error) - } if (responded) return - // error object may be untransferable via postMessage, so it will be ignored - lastError = { - code: EErrorCode.HANDLER_EXEC_ERROR, - // @ts-expect-error ignore - message: error.message || error.stack, - } + lastError = this.handlerError(error, method, newArgs) } } count += 1 @@ -419,10 +531,25 @@ export abstract class AbstractHub { }) } + private handlerError(error: unknown, method: IFn | IFn[], args: any[]): IError { + if (ENABLE_DEBUG) console.warn('[duplex-message] run handler error', method, 'with arguments', args, error) + // Thrown values can be uncloneable, have throwing getters, or lack string conversion. + let message = 'handler threw an unreadable error' + try { + message = String(error instanceof Error ? error.message : ( + error && typeof error === 'object' + ? (error as { message?: unknown; stack?: unknown }).message + ?? (error as { stack?: unknown }).stack ?? error + : error + )) + } catch { /* Keep error conversion from stranding the call. */ } + return { code: EErrorCode.HANDLER_EXEC_ERROR, message } + } + /** * get message callbacks * * return a tuple of [callbacks, isGeneral], or undefined when no callbacks found - * * when General is true, callbacks will receive methodName as the first argument + * * when General is true, callbacks will receive methodName as the first argument */ protected getMessageCallbacks(peer: any, reqMsg: IRequest) { const { methodName } = reqMsg @@ -445,8 +572,24 @@ export abstract class AbstractHub { ...args: any[] ) { this.checkInstance() + const controls = typeof methodName === 'string' ? undefined : methodName + const requestTimeout = controls?.requestTimeout ?? this._requestTimeout + const signal = controls?.signal + if ((controls && !AbstractHub.isValidRequestTimeout(requestTimeout)) + || (signal !== undefined && (signal === null || typeof signal !== 'object' + || typeof signal.aborted !== 'boolean' + || typeof signal.addEventListener !== 'function' + || typeof signal.removeEventListener !== 'function'))) { + return Promise.reject({ code: EErrorCode.INVALID_MESSAGE, message: 'invalid requestTimeout or signal' }) + } + if (signal?.aborted) { + return Promise.reject({ code: EErrorCode.REQUEST_ABORTED, message: 'request has been aborted' }) + } const reqMsg = this.buildReqMessage(methodName, args) const result = new Promise((resolve, reject) => { + const pending: IPendingRequest = { peer, reject } + this._pendingRequests.set(reqMsg.messageID, pending) + this._pendingPeerCounts.set(peer, (this._pendingPeerCounts.get(peer) || 0) + 1) // 0 for not match // 1 for response, done // 2 for progress, need to be continue @@ -473,9 +616,37 @@ export abstract class AbstractHub { } return 0 } - this.listenResponse(peer, reqMsg, callback) + try { + this.listenResponse(peer, reqMsg, callback) + if (requestTimeout > 0) { + const timer = setTimeout(() => { + this.rejectPendingRequest(reqMsg.messageID, { + code: EErrorCode.REQUEST_TIMEOUT, + message: `request timed out after ${requestTimeout}ms for method ${reqMsg.methodName}`, + }) + }, requestTimeout) + pending.requestTimer = timer + } + if (signal) { + const abort = () => this.rejectPendingRequest(reqMsg.messageID, { + code: EErrorCode.REQUEST_ABORTED, + message: 'request has been aborted', + }) + pending.abortCleanup = () => signal.removeEventListener('abort', abort) + signal.addEventListener('abort', abort, { once: true }) + // Also handle cancellation that occurred during listener registration. + if (signal.aborted) abort() + } + } catch { + this.rejectPendingRequest(reqMsg.messageID, { + code: EErrorCode.INVALID_MESSAGE, + message: 'unable to set up request', + }) + } }) + if (!this._pendingRequests.has(reqMsg.messageID)) return result try { + this.onRequestStarted(peer) this.sendMessage(peer, AbstractHub.normalizeRequest(peer, reqMsg)) } catch (error) { if (ENABLE_DEBUG) { @@ -484,8 +655,7 @@ export abstract class AbstractHub { error, ', message:', reqMsg, ) } - delete this._responseCallbackMap[reqMsg.messageID] - return Promise.reject({ + this.rejectPendingRequest(reqMsg.messageID, { code: EErrorCode.INVALID_MESSAGE, message: 'unable to send message', }) @@ -510,28 +680,36 @@ export abstract class AbstractHub { this._responseCallbackMap[reqMsg.messageID] = wrappedCallback // timeout when no response, callback get a failure - setTimeout(() => { - if (this._designedResponse[reqMsg.messageID]) return + const timer = setTimeout(() => { const resp = this._buildRespMessage( { code: EErrorCode.METHOD_NOT_FOUND, message: `no corresponding handler found for method ${reqMsg.methodName}` }, reqMsg, false, ) - this.runResponseCallback(resp) + callback(resp) + this.clearPendingRequest(reqMsg.messageID) }, this._heartbeatTimeout) + this._pendingRequests.get(reqMsg.messageID)!.heartbeatTimer = timer } protected buildReqMessage( methodName: string | IMethodNameConfig, args: any[], ): IRequest { - const basicCfg = typeof methodName === 'string' ? { methodName } : methodName + let messageConfig: IMethodNameConfig | undefined + if (typeof methodName !== 'string') { + // These controls belong to the caller and must never be serialized to the peer. + const { signal: _signal, requestTimeout: _requestTimeout, ...config } = methodName + messageConfig = config + } const options = args[0] const progress = Boolean( options && typeof options.onprogress === 'function', ) - return Object.assign(basicCfg, { + return { + ...messageConfig, + methodName: typeof methodName === 'string' ? methodName : messageConfig!.methodName, from: this.instanceID, // toInstance, // eslint-disable-next-line no-plusplus @@ -539,7 +717,7 @@ export abstract class AbstractHub { type: 'request' as const, data: args, progress, - }) + } } protected _buildRespMessage( @@ -557,22 +735,28 @@ export abstract class AbstractHub { } } - protected buildProgressMessage(data: any, reqMsg: IRequest): IProgress { + protected buildProgressMessage(data: any, reqMsg: IRequest, heartbeat = false): IProgress { return { from: this.instanceID, to: reqMsg.from, messageID: reqMsg.messageID, type: 'progress', + heartbeat, data, } } protected isMessage(msg: any): msg is IMessageBase { - return msg + return !!(msg + && typeof msg === 'object' && (msg.to === this.instanceID || !msg.to) - && msg.messageID - && msg.from - && msg.type + && Number.isSafeInteger(msg.messageID) && msg.messageID > 0 + && typeof msg.from === 'string' && msg.from.length > 0 + && (msg.to === undefined || typeof msg.to === 'string') + && (msg.type === 'request' + ? typeof msg.methodName === 'string' && Array.isArray(msg.data) + : msg.type === 'response' ? typeof msg.isSuccess === 'boolean' + : msg.type === 'progress' && (msg.heartbeat === undefined || typeof msg.heartbeat === 'boolean'))) } protected isRequestMessage(msg: any): msg is IRequest { @@ -598,6 +782,13 @@ export abstract class AbstractHub { && msg.type === 'progress' } + private isHeartbeatMessage(reqMsg: IRequest, msg: IResponse | IProgress) { + // wrapResponseCallback has already validated the message's shape. + return msg.type === 'progress' && msg.to === reqMsg.from && msg.messageID === reqMsg.messageID + && (msg.heartbeat === true + || (msg.heartbeat === undefined && msg.data === CONTINUE_INDICATOR)) + } + protected static wrapResponseCallback( instance: AbstractHub, reqMsg: IRequest, @@ -605,13 +796,13 @@ export abstract class AbstractHub { ) { return (resp: IResponse | IProgress) => { if (!instance.isMessage(resp)) return 0 + if (reqMsg.to && resp.from !== reqMsg.to) return 0 const designedPeerID = instance._designedResponse[reqMsg.messageID] // ignore not designed resp if (designedPeerID && resp.from !== designedPeerID) { if ( ENABLE_DEBUG - && instance.isProgressMessage(reqMsg, resp) - && resp.data === CONTINUE_INDICATOR + && instance.isHeartbeatMessage(reqMsg, resp) ) { console.warn( '[duplex-message] message', @@ -624,7 +815,7 @@ export abstract class AbstractHub { return 0 } - if (instance.isProgressMessage(reqMsg, resp) && resp.data === CONTINUE_INDICATOR) { + if (instance.isHeartbeatMessage(reqMsg, resp)) { if (!designedPeerID) { // eslint-disable-next-line no-param-reassign instance._designedResponse[reqMsg.messageID] = resp.from @@ -662,8 +853,9 @@ export abstract class AbstractHub { if (typeof handlerMap === 'function') { return [handlerMap, true] } - const callbacks = handlerMap[methodName] - return callbacks ? [callbacks, false] : undefined + const callbacks = Object.prototype.hasOwnProperty.call(handlerMap, methodName) + ? handlerMap[methodName] : undefined + return callbacks?.length ? [callbacks, false] : undefined } protected static generateInstanceID() { @@ -672,19 +864,20 @@ export abstract class AbstractHub { .slice(0, -1) } + private static isValidRequestTimeout(timeout: number) { + return Number.isFinite(timeout) && timeout >= 0 && timeout <= 2147483647 + } + private static mergeEventMap( existingMap: IHandlerMapInner, newMap: IHandlerMap, ): IHandlerMapInner { - const result = { ...existingMap } + const result = Object.assign(Object.create(null), existingMap) as IHandlerMapInner const newKeys = Object.keys(newMap) newKeys.forEach((key) => { const callbacks = Array.isArray(newMap[key]) ? newMap[key] : [newMap[key]] - if (existingMap[key]) { - result[key].push(...callbacks) - } else { - result[key] = callbacks - } + const existing = Object.prototype.hasOwnProperty.call(existingMap, key) ? existingMap[key] : [] + result[key] = [...(existing || []), ...callbacks] }) return result } diff --git a/packages/duplex-message/src/broadcast-message.ts b/packages/duplex-message/src/broadcast-message.ts index a74d629..c02086c 100644 --- a/packages/duplex-message/src/broadcast-message.ts +++ b/packages/duplex-message/src/broadcast-message.ts @@ -92,7 +92,7 @@ export class BroadcastMessageHub extends AbstractHub { /** shared PostMessageHub instance */ public static get shared() { - if (!sharedMessageHub) { + if (!sharedMessageHub || sharedMessageHub.isDestroyed) { sharedMessageHub = new BroadcastMessageHub() } return sharedMessageHub diff --git a/packages/duplex-message/src/index.ts b/packages/duplex-message/src/index.ts index aba337d..f5f2ea9 100644 --- a/packages/duplex-message/src/index.ts +++ b/packages/duplex-message/src/index.ts @@ -3,3 +3,5 @@ export * from './post-message' export * from './storage-message' export * from './page-script-message' export * from './broadcast-message' +export * from './rpc-client' +export * from './readiness' diff --git a/packages/duplex-message/src/page-script-message.ts b/packages/duplex-message/src/page-script-message.ts index 0c3cc6b..3d82649 100644 --- a/packages/duplex-message/src/page-script-message.ts +++ b/packages/duplex-message/src/page-script-message.ts @@ -89,7 +89,7 @@ export class PageScriptMessageHub extends AbstractHub { /** shared PageScriptMessageHub instance */ public static get shared() { - if (!sharedMessageHub) { + if (!sharedMessageHub || sharedMessageHub.isDestroyed) { sharedMessageHub = new PageScriptMessageHub() } return sharedMessageHub diff --git a/packages/duplex-message/src/post-message.ts b/packages/duplex-message/src/post-message.ts index 41c51f4..16eeff3 100644 --- a/packages/duplex-message/src/post-message.ts +++ b/packages/duplex-message/src/post-message.ts @@ -21,6 +21,13 @@ export interface IPostMessageMethodOptions extends IMethodNameConfig { targetOrigin?: string } +export interface IPostMessageHubOptions extends IAbstractHubOptions { + /** Allowed origins for incoming window messages. Omitted preserves legacy behavior. */ + allowedOrigins?: readonly string[] + /** Default origin for outgoing window messages. Default '*'; set an explicit trusted origin. */ + targetOrigin?: string +} + type IOwnPeer = Window | Worker | undefined let sharedMessageHub: PostMessageHub @@ -36,14 +43,30 @@ function isWindow(peer:any): peer is Window { } export class PostMessageHub extends AbstractHub { + private readonly _allowedOrigins?: readonly string[] + + private readonly _targetOrigin: string + + private readonly _messageOrigins?: WeakMap protected _hostedWorkers: Worker[] protected readonly _WIN: Window | Worker protected readonly _isInWorker: boolean - constructor(options?: IAbstractHubOptions) { + constructor(options?: IPostMessageHubOptions) { super(options) + if (options?.allowedOrigins !== undefined && (!Array.isArray(options.allowedOrigins) + || options.allowedOrigins.some(origin => typeof origin !== 'string' || !origin.length))) { + throw new TypeError('allowedOrigins must be an array of origin strings') + } + if (options?.targetOrigin !== undefined + && (typeof options.targetOrigin !== 'string' || !options.targetOrigin.length)) { + throw new TypeError('targetOrigin must be an origin string') + } + this._allowedOrigins = options?.allowedOrigins?.slice() + if (this._allowedOrigins) this._messageOrigins = new WeakMap() + this._targetOrigin = options?.targetOrigin ?? '*' this._hostedWorkers = [] // save current window it's self // eslint-disable-next-line no-restricted-globals @@ -88,13 +111,13 @@ export class PostMessageHub extends AbstractHub { */ emit(peer: Window | Worker, methodName: string | IPostMessageMethodOptions, ...args: any[]) { + this.checkInstance() if (isWindow(peer) && !peer.parent) { return Promise.reject({ code: EErrorCode.PEER_NOT_FOUND, message: 'peer window is unloaded', }) } - this._addWorkerListener(peer) return this._emit(peer, methodName, ...args) } @@ -106,8 +129,24 @@ export class PostMessageHub extends AbstractHub { */ off(peer: Window | Worker | '*', methodName?: string, handler?: IFn) { super._off(peer, methodName, handler) - const evtMpIndx = this._eventHandlerMap.findIndex((m) => m[0] === peer) - if (evtMpIndx === -1 && isWorker(peer)) { + if (peer === '*') { + this._hostedWorkers.slice().forEach((worker) => this._removeUnusedWorkerListener(worker)) + } else { + this._removeUnusedWorkerListener(peer) + } + } + + protected override onRequestSettled(peer: Window | Worker | '*') { + this._removeUnusedWorkerListener(peer) + } + + protected override onRequestStarted(peer: Window | Worker | '*') { + this._addWorkerListener(peer) + } + + private _removeUnusedWorkerListener(peer: Window | Worker | '*') { + if (isWorker(peer) && !this.getEventHandlers(peer) && !this.getEventHandlers('*') + && !this.hasPendingRequests(peer)) { const idx = this._hostedWorkers.indexOf(peer) if (idx > -1) { this._hostedWorkers.splice(idx, 1) @@ -242,9 +281,36 @@ export class PostMessageHub extends AbstractHub { protected _onMessageReceived(evt: MessageEvent) { const peer = evt.source || evt.currentTarget || this._WIN + // Worker messages have no Window source or meaningful origin. + if (this._allowedOrigins && isWindow(evt.source)) { + if (!this._allowedOrigins.includes('*') && !this._allowedOrigins.includes(evt.origin)) return + if (evt.origin !== 'null') { + // Remember each request separately: a Window can navigate between accepted origins. + if (evt.data && typeof evt.data === 'object' && evt.data.type === 'request') { + this._messageOrigins!.set(evt.data, evt.origin) + } + } + } this.onMessage(peer, evt.data) } + protected override _buildRespMessage(data: any, request: IRequest, isSuccess: boolean): IResponse { + const response = super._buildRespMessage(data, request, isSuccess) + this.copyMessageOrigin(request, response) + return response + } + + protected override buildProgressMessage(data: any, request: IRequest, heartbeat = false): IProgress { + const progress = super.buildProgressMessage(data, request, heartbeat) + this.copyMessageOrigin(request, progress) + return progress + } + + private copyMessageOrigin(request: IRequest, response: IResponse | IProgress) { + const origin = this._messageOrigins?.get(request) + if (origin !== undefined) this._messageOrigins!.set(response, origin) + } + protected sendMessage( peer: Window | Worker, msg: IRequest | IResponse | IProgress, @@ -252,7 +318,7 @@ export class PostMessageHub extends AbstractHub { const args: any[] = [msg] if (!this._isInWorker && isWindow(peer)) { // @ts-ignore - args.push(msg.targetOrigin || '*') + args.push(msg.targetOrigin || this._messageOrigins?.get(msg) || this._targetOrigin) } // add transferable data if exists // @ts-ignore @@ -266,7 +332,7 @@ export class PostMessageHub extends AbstractHub { /** shared PostMessageHub instance */ public static get shared() { - if (!sharedMessageHub) { + if (!sharedMessageHub || sharedMessageHub.isDestroyed) { sharedMessageHub = new PostMessageHub() } return sharedMessageHub diff --git a/packages/duplex-message/src/readiness.ts b/packages/duplex-message/src/readiness.ts new file mode 100644 index 0000000..9790ed6 --- /dev/null +++ b/packages/duplex-message/src/readiness.ts @@ -0,0 +1,109 @@ +import { EErrorCode, IError, IMethodNameConfig } from './abstract' + +/** Register this endpoint after the peer's application handlers are ready. */ +export const READY_METHOD = '__duplex_message_ready__' + +export interface IWaitForPeerOptions { + /** Total wait in milliseconds. Default 5000. */ + timeout?: number + /** Probe timeout and pause between attempts, in milliseconds. Default 100. */ + interval?: number + signal?: AbortSignal +} + +const abortedError = (): IError => ({ code: EErrorCode.REQUEST_ABORTED, message: 'waiting for peer was aborted' }) + +function validateDuration(value: number) { + return Number.isFinite(value) && value > 0 && value <= 2147483647 +} + +function probeWithSignal(probe: () => Promise, signal: AbortSignal) { + return new Promise((resolve, reject) => { + let settled = false + const finish = (callback: (value: any) => void, value: unknown) => { + if (settled) return + settled = true + signal.removeEventListener('abort', abort) + callback(value) + } + const abort = () => finish(reject, abortedError()) + if (signal.aborted) { abort(); return } + signal.addEventListener('abort', abort, { once: true }) + if (signal.aborted) { abort(); return } + try { + Promise.resolve(probe()).then(value => finish(resolve, value), error => finish(reject, error)) + } catch (error) { finish(reject, error) } + }) +} + +function pause(duration: number, signal: AbortSignal) { + return new Promise((resolve, reject) => { + const abort = () => { + clearTimeout(timer) + signal.removeEventListener('abort', abort) + reject(abortedError()) + } + const timer = setTimeout(() => { + signal.removeEventListener('abort', abort) + resolve() + }, duration) + signal.addEventListener('abort', abort, { once: true }) + if (signal.aborted) abort() + }) +} + +/** + * Wait for an explicit ready endpoint; never retries a business operation. + * The peer must register READY_METHOD with a handler returning true when ready. + */ +export async function waitForPeer( + emit: (method: IMethodNameConfig) => Promise, + options: IWaitForPeerOptions = {}, +): Promise { + const { timeout = 5000, interval = 100, signal } = options + if (!validateDuration(timeout) || !validateDuration(interval) + || (signal !== undefined && (signal === null || typeof signal !== 'object' + || typeof signal.aborted !== 'boolean' || typeof signal.addEventListener !== 'function' + || typeof signal.removeEventListener !== 'function'))) { + throw { code: EErrorCode.INVALID_MESSAGE, message: 'invalid readiness options' } satisfies IError + } + if (signal?.aborted) throw abortedError() + const controller = new AbortController() + let stopReason: IError | undefined + const stop = (error: IError) => { + if (controller.signal.aborted) return + stopReason = error + controller.abort() + } + const forwardAbort = () => stop(abortedError()) + const timer = setTimeout(() => stop({ + code: EErrorCode.REQUEST_TIMEOUT, message: `peer was not ready within ${timeout}ms`, + }), timeout) + try { + signal?.addEventListener('abort', forwardAbort, { once: true }) + if (signal?.aborted) forwardAbort() + while (!controller.signal.aborted) { + try { + const ready = await probeWithSignal(() => emit({ + methodName: READY_METHOD, requestTimeout: Math.min(interval, timeout), signal: controller.signal, + }), controller.signal) + if (controller.signal.aborted) break + if (ready === true) return + if (ready !== false) throw { + code: EErrorCode.INVALID_MESSAGE, message: 'ready endpoint must return a boolean', + } satisfies IError + } catch (error) { + if (controller.signal.aborted) break + const code = (error as IError | null)?.code + if (code !== EErrorCode.METHOD_NOT_FOUND && code !== EErrorCode.REQUEST_TIMEOUT) throw error + } + await pause(interval, controller.signal) + } + throw stopReason + } catch (error) { + throw stopReason ?? error + } finally { + clearTimeout(timer) + signal?.removeEventListener('abort', forwardAbort) + } +} diff --git a/packages/duplex-message/src/rpc-client.ts b/packages/duplex-message/src/rpc-client.ts new file mode 100644 index 0000000..ae592d2 --- /dev/null +++ b/packages/duplex-message/src/rpc-client.ts @@ -0,0 +1,20 @@ +import { IFn, IMethodNameConfig } from './abstract' + +export type RpcEmitter = (method: string | IMethodNameConfig, ...args: any[]) => Promise + +// Avoid requiring the built-in Awaited type (added in TypeScript 4.5). +type RpcResult = Value extends PromiseLike ? RpcResult : Value + +export interface IRpcClient> { + call>( + method: Key | (IMethodNameConfig & { methodName: Key }), + ...args: Parameters + ): Promise>> +} + +/** Add method-name, argument and result types to any hub without changing its wire protocol. */ +export function createRpcClient>( + emit: RpcEmitter, +): IRpcClient { + return { call: (method, ...args) => emit(method, ...args) as Promise } +} diff --git a/packages/duplex-message/src/storage-message.ts b/packages/duplex-message/src/storage-message.ts index 6a3c2d6..2332459 100644 --- a/packages/duplex-message/src/storage-message.ts +++ b/packages/duplex-message/src/storage-message.ts @@ -81,12 +81,11 @@ export class StorageMessageHub extends AbstractHub { const msgKey = this._getMsgKey(msg) try { localStorage.setItem(msgKey, JSON.stringify(msg)) - // clean storage key after 100ms - setTimeout(() => { - localStorage.removeItem(msgKey) - }, 100) + // Storage events retain newValue even after the key is removed. + // Immediate cleanup also allows consecutive identical progress updates. + localStorage.removeItem(msgKey) } catch (e) { - if (process.env.NODE_ENV !== 'production') { + if (typeof process !== 'undefined' && process.env.NODE_ENV !== 'production') { console.warn( '[duplex-message] unable to stringify message, message not sent', e, 'message:', msg, @@ -113,7 +112,7 @@ export class StorageMessageHub extends AbstractHub { const msg = JSON.parse(evt.newValue) // eslint-disable-next-line consistent-return return msg - } catch (error) { + } catch { /** */ } } @@ -126,7 +125,7 @@ export class StorageMessageHub extends AbstractHub { /** shared StorageMessageHub instance */ public static get shared() { - if (!sharedMessageHub) { + if (!sharedMessageHub || sharedMessageHub.isDestroyed) { sharedMessageHub = new StorageMessageHub() } return sharedMessageHub diff --git a/packages/duplex-message/test/abstract/index.tn.ts b/packages/duplex-message/test/abstract/index.tn.ts index 3a22701..503c25b 100644 --- a/packages/duplex-message/test/abstract/index.tn.ts +++ b/packages/duplex-message/test/abstract/index.tn.ts @@ -184,8 +184,9 @@ describe('duplex-message abstract instance utils methods', () => { [{}, false], [{to: 'test', messageID: 'abc', type: 'progress'}, false], [{to: 'test', from: 'xxx', messageID: 'abc', type: 'progress'}, false], - [{to: 'test', from: 'xxx', messageID: 'abc', type: 'request'}, true], - [{data: 'test', from: 'xxx', messageID: 'abc', type: 'request'}, true], + [{to: 'test', from: 'xxx', messageID: 'abc', type: 'request'}, false], + [{data: 'test', from: 'xxx', messageID: 'abc', type: 'request'}, false], + [{to: 'test', from: 'xxx', messageID: 1, type: 'request', methodName: 'greet', data: []}, true], ] testsMaps.forEach(([msg, expected]) => { @@ -194,4 +195,4 @@ describe('duplex-message abstract instance utils methods', () => { }) }) -}) \ No newline at end of file +}) diff --git a/packages/duplex-message/test/abstract/lifecycle.tn.ts b/packages/duplex-message/test/abstract/lifecycle.tn.ts new file mode 100644 index 0000000..5c787c5 --- /dev/null +++ b/packages/duplex-message/test/abstract/lifecycle.tn.ts @@ -0,0 +1,542 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { AbstractHub, EErrorCode, type IAbstractHubOptions, type IFn, type IHandlerMap, type IMethodNameConfig, type IRequest, type IResponse, type IProgress } from '../../src/abstract' + +class TestHub extends AbstractHub { + readonly sent: (IRequest | IResponse | IProgress)[] = [] + failSend = false + + on(peer: TestHub | '*', method: string | IHandlerMap, handler?: IFn) { + if (typeof method === 'string') this._on(peer, method, handler!) + else this._on(peer, method) + } + + off(peer: TestHub | '*', method?: string, handler?: IFn) { + this._off(peer, method, handler) + } + + emit(peer: TestHub, method: string | IMethodNameConfig, ...args: any[]) { + return this._emit(peer, method, ...args) + } + + receive(peer: TestHub, message: unknown) { + return this.onMessage(peer, message) + } + + get pendingCount() { return Object.keys(this._responseCallbackMap).length } + + protected sendMessage(peer: TestHub, message: IRequest | IResponse | IProgress) { + if (this.failSend) throw new Error('transport unavailable') + const copied = structuredClone(message) + this.sent.push(copied) + void peer.receive(this, copied) + } +} + +const hubs: TestHub[] = [] +function hub(options?: IAbstractHubOptions) { + const result = new TestHub(options) + hubs.push(result) + return result +} + +beforeEach(() => vi.useFakeTimers()) +afterEach(() => { + hubs.splice(0).forEach((item) => item.destroy()) + vi.useRealTimers() +}) + +describe('request lifecycle', () => { + it('returns a rejected Promise when a custom message predicate throws', async () => { + class ThrowingHub extends TestHub { + protected override isMessage(_message: unknown): _message is IRequest { + throw new Error('custom validation failed') + } + } + const custom = new ThrowingHub() + hubs.push(custom) + await expect(custom.receive(hub(), {})).rejects.toThrow('custom validation failed') + }) + + it.each([ + ['undefined', (): unknown => undefined], ['null', (): unknown => null], + ['object', (): unknown => ({ value: 42 })], ['promise', (): unknown => Promise.resolve(42)], + ] as const)('preserves single-handler %s results', async (_name, value) => { + const client = hub(), server = hub() + server.on(client, 'value', value) + const expected = await value() + await expect(client.emit(server, 'value')).resolves.toEqual(expected) + expect(vi.getTimerCount()).toBe(0) + }) + + it('assimilates nested thenables and reads a then getter only once', async () => { + const client = hub(), server = hub() + let reads = 0 + server.on(client, 'value', () => ({ + // oxlint-disable-next-line unicorn/no-thenable -- Intentional Promise assimilation fixture. + get then() { + reads++ + return (resolve: (value: Promise) => void) => resolve(Promise.resolve(42)) + }, + })) + await expect(client.emit(server, 'value')).resolves.toBe(42) + expect(reads).toBe(1) + }) + + it('converts a throwing then getter to a handler error', async () => { + const client = hub(), server = hub() + server.on(client, 'value', () => ({ + // oxlint-disable-next-line unicorn/no-thenable -- Intentional Promise assimilation fixture. + get then(): never { throw new Error('bad then') }, + })) + await expect(client.emit(server, 'value')).rejects.toMatchObject({ code: 1, message: 'bad then' }) + expect(vi.getTimerCount()).toBe(0) + }) + + it('keeps multi-handler first-defined results, undefined success, and last-error semantics', async () => { + const client = hub(), server = hub() + let finish!: (value: number) => void + server.on(client, { race: [() => undefined, () => Promise.reject(new Error('ignored')), () => new Promise(resolve => { finish = resolve })] }) + const pending = client.emit(server, 'race') + finish(42) + await expect(pending).resolves.toBe(42) + server.on(client, { empty: [() => undefined, () => undefined] }) + await expect(client.emit(server, 'empty')).resolves.toBeUndefined() + server.on(client, { fail: [() => { throw new Error('first') }, () => Promise.reject(new Error('last'))] }) + await expect(client.emit(server, 'fail')).rejects.toMatchObject({ code: 1, message: 'last' }) + }) + + it('reuses frozen method options without mutating the caller or earlier requests', async () => { + const client = hub() + const server = hub() + server.on(client, 'echo', (value: number) => value) + const options = Object.freeze({ methodName: 'echo', to: server.instanceID }) + expect(await Promise.all([client.emit(server, options, 1), client.emit(server, options, 2)])).toEqual([1, 2]) + expect(options).toEqual({ methodName: 'echo', to: server.instanceID }) + expect(client.sent.map((request) => request.messageID)).toEqual([1, 2]) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('does not retain heartbeat timers after 1000 completed requests', async () => { + const client = hub() + const server = hub() + server.on(client, 'echo', (value: number) => value) + const results = await Promise.all(Array.from({ length: 1000 }, (_, i) => client.emit(server, 'echo', i))) + expect(results).toHaveLength(1000) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('clears the timer on heartbeat while allowing handlers to run beyond the timeout', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + let finish!: (value: string) => void + server.on(client, 'slow', () => new Promise((resolve) => { finish = resolve })) + const response = client.emit(server, 'slow') + expect(vi.getTimerCount()).toBe(0) + await vi.advanceTimersByTimeAsync(1000) + finish('done') + await expect(response).resolves.toBe('done') + }) + + it('preserves the legacy 500ms fallback for a zero heartbeat timeout', async () => { + const client = hub({ heartbeatTimeout: 0 }) + const response = expect(client.emit(hub(), 'missing')).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await vi.advanceTimersByTimeAsync(499) + expect(client.pendingCount).toBe(1) + await vi.advanceTimersByTimeAsync(1) + await response + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('rejects the original promise and removes all pending state when sending fails', async () => { + const client = hub() + client.failSend = true + await expect(client.emit(hub(), 'echo')).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('rejects outstanding requests on destroy, including acknowledged requests', async () => { + const client = hub() + const server = hub() + server.on(client, 'slow', () => new Promise(() => {})) + const acknowledged = expect(client.emit(server, 'slow')).rejects.toMatchObject({ code: EErrorCode.UNKNOWN }) + const waiting = expect(client.emit(server, 'missing')).rejects.toMatchObject({ code: EErrorCode.UNKNOWN }) + client.destroy() + client.destroy() + await Promise.all([acknowledged, waiting]) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + expect(() => client.emit(server, 'slow')).toThrow('destroyed') + }) + + it('does not send late progress or responses after the responder is destroyed', async () => { + const client = hub() + const server = hub() + let finish!: () => void + let progress!: (value: number) => void + server.on(client, 'slow', (options: { onprogress: (value: number) => void }) => { + progress = options.onprogress + return new Promise((resolve) => { finish = resolve }) + }) + const response = expect(client.emit(server, 'slow', { onprogress: vi.fn() })).rejects.toMatchObject({ code: EErrorCode.UNKNOWN }) + server.destroy() + progress(1) + finish() + await Promise.resolve() + await Promise.resolve() + expect(server.sent).toHaveLength(1) + client.destroy() + await response + await expect(server.receive(client, {})).resolves.toBeUndefined() + }) + + it('converts uncloneable handler responses to an error instead of hanging', async () => { + const client = hub() + const server = hub() + server.on(client, 'uncloneable', () => () => {}) + await expect(client.emit(server, 'uncloneable')).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(client.pendingCount).toBe(0) + }) + + it.each([ + ['null', (): unknown => null], ['undefined', (): unknown => undefined], ['string', (): unknown => 'failure'], + ['stack object', (): unknown => ({ stack: 'custom stack' })], ['null prototype', (): unknown => Object.create(null)], + ['throwing getter', (): object => ({ get message(): string { throw new Error('unreadable message') } })], + ] as const)('handles non-Error throws: %s', async (_name, createError) => { + const client = hub() + const server = hub() + server.on(client, 'fail', () => { throw createError() }) + await expect(client.emit(server, 'fail')).rejects.toMatchObject({ code: EErrorCode.HANDLER_EXEC_ERROR, message: expect.any(String) }) + }) + + it('ignores forged responses from a different transport peer', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + const spoof = { from: server.instanceID, to: client.instanceID, messageID: 1, type: 'response', isSuccess: true, data: 'forged' } + const response = expect(client.emit(server, 'missing')).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await client.receive(hub(), spoof) + expect(client.pendingCount).toBe(1) + await vi.advanceTimersByTimeAsync(10) + await response + }) + + it('ignores a response with the wrong instance ID for an addressed request', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + const response = expect(client.emit(server, { methodName: 'missing', to: server.instanceID })).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await client.receive(server, { from: 'other', to: client.instanceID, messageID: 1, type: 'progress', data: '--message-hub-to-be-continued--' }) + expect(vi.getTimerCount()).toBe(1) + await vi.advanceTimersByTimeAsync(10) + await response + }) +}) + +describe('handler and message isolation', () => { + it('registers prototype-like method names without treating inherited properties as handlers', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + server.on(client, 'echo', () => 'echo') + const missing = expect(client.emit(server, 'constructor')).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await vi.advanceTimersByTimeAsync(10) + await missing + for (const method of ['constructor', 'toString', '__proto__']) { + server.on(client, method, () => method) + await expect(client.emit(server, method)).resolves.toBe(method) + server.off(client, method) + } + }) + + it('does not mutate caller-owned handler arrays', async () => { + const client = hub() + const server = hub() + const handlers = Object.freeze([() => 'first']) + server.on(client, { echo: handlers as unknown as IFn[] }) + server.on(client, 'echo', () => 'second') + expect(handlers).toHaveLength(1) + await expect(client.emit(server, 'echo')).resolves.toBe('first') + }) + + it('does not acknowledge an empty handler list', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + server.on(client, { empty: [] }) + const response = expect(client.emit(server, 'empty')).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await vi.advanceTimersByTimeAsync(10) + await response + expect(server.sent).toHaveLength(0) + }) + + it('ignores malformed messages without invoking handlers or rejecting event listeners', async () => { + const client = hub() + const server = hub() + const handler = vi.fn() + server.on(client, 'echo', handler) + const base = { from: client.instanceID, messageID: 1, type: 'request', methodName: 'echo', data: [] } + for (const message of [null, 1, 'text', {}, { ...base, data: null }, { ...base, methodName: 1 }, { ...base, messageID: '1' }, { ...base, type: 'other' }]) { + await expect(server.receive(client, message)).resolves.toBeUndefined() + } + expect(handler).not.toHaveBeenCalled() + expect(server.sent).toHaveLength(0) + }) +}) + +describe('heartbeat protocol compatibility', () => { + it('delivers the legacy heartbeat string and heartbeat-shaped objects as business progress', async () => { + const client = hub() + const server = hub() + const onprogress = vi.fn() + const indicator = '--message-hub-to-be-continued--' + server.on(client, 'download', (options: { onprogress: IFn }) => { + options.onprogress(indicator) + options.onprogress({ heartbeat: true, data: indicator }) + return 'done' + }) + await expect(client.emit(server, 'download', { onprogress })).resolves.toBe('done') + expect(onprogress.mock.calls).toEqual([[indicator], [{ heartbeat: true, data: indicator }]]) + // Older callers still identify this same sentinel as the handshake. + expect(server.sent[0]).toMatchObject({ type: 'progress', heartbeat: true, data: indicator }) + expect(server.sent[1]).toMatchObject({ type: 'progress', heartbeat: false, data: indicator }) + expect(vi.getTimerCount()).toBe(0) + }) + + it('accepts an unmarked legacy heartbeat and waits beyond the heartbeat timeout', async () => { + class LegacyHub extends TestHub { + protected override buildProgressMessage(data: any, request: IRequest) { + const message = super.buildProgressMessage(data, request) + delete message.heartbeat + return message + } + } + const client = hub({ heartbeatTimeout: 10 }) + const server = new LegacyHub() + hubs.push(server) + let finish!: (value: string) => void + server.on(client, 'slow', () => new Promise((resolve) => { finish = resolve })) + const response = client.emit(server, 'slow') + await vi.advanceTimersByTimeAsync(1000) + expect(client.pendingCount).toBe(1) + expect(vi.getTimerCount()).toBe(0) + finish('legacy result') + await expect(response).resolves.toBe('legacy result') + }) + + it('ignores malformed heartbeat markers without acknowledging the request', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const server = hub() + const response = expect(client.emit(server, 'missing')).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await client.receive(server, { + from: server.instanceID, to: client.instanceID, messageID: 1, + type: 'progress', heartbeat: 'true', data: '--message-hub-to-be-continued--', + }) + expect(vi.getTimerCount()).toBe(1) + await vi.advanceTimersByTimeAsync(10) + await response + }) +}) + +describe('local request controls', () => { + it('does not send or allocate timers for an already aborted signal', async () => { + const client = hub() + const controller = new AbortController() + controller.abort() + await expect(client.emit(hub(), { + methodName: 'missing', signal: controller.signal, requestTimeout: 100, + })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(client.sent).toHaveLength(0) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('aborts before heartbeat and removes the abort listener and both timers', async () => { + const client = hub() + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + const response = expect(client.emit(hub(), { + methodName: 'missing', signal: controller.signal, requestTimeout: 100, + })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(vi.getTimerCount()).toBe(2) + controller.abort() + await response + expect(remove).toHaveBeenCalledOnce() + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('aborts after heartbeat, ignores late progress/results, and leaves the peer handler running', async () => { + const client = hub() + const server = hub() + const controller = new AbortController() + const onprogress = vi.fn() + let finish!: () => void + let progress!: IFn + server.on(client, 'slow', (options: { onprogress: IFn }) => { + progress = options.onprogress + return new Promise((resolve) => { finish = resolve }) + }) + const response = expect(client.emit(server, { + methodName: 'slow', signal: controller.signal, requestTimeout: 100, + }, { onprogress })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(vi.getTimerCount()).toBe(1) + controller.abort() + await response + progress('late') + finish() + await Promise.resolve() + await Promise.resolve() + expect(onprogress).not.toHaveBeenCalled() + expect(server.sent.some((message) => message.type === 'response')).toBe(true) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + server.on(client, 'echo', () => 'still usable') + await expect(client.emit(server, 'echo')).resolves.toBe('still usable') + }) + + it('times out after heartbeat even if progress continues, and detaches the abort listener', async () => { + const client = hub() + const server = hub() + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + const onprogress = vi.fn() + let progress!: IFn + server.on(client, 'slow', (options: { onprogress: IFn }) => { + progress = options.onprogress + return new Promise(() => {}) + }) + const response = expect(client.emit(server, { + methodName: 'slow', requestTimeout: 30, signal: controller.signal, + }, { onprogress })).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + await vi.advanceTimersByTimeAsync(20) + progress('working') + expect(onprogress).toHaveBeenCalledWith('working') + await vi.advanceTimersByTimeAsync(10) + await response + progress('late') + expect(onprogress).toHaveBeenCalledTimes(1) + expect(remove).toHaveBeenCalledOnce() + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('counts heartbeat wait toward the total request deadline', async () => { + const client = hub({ heartbeatTimeout: 100 }) + const response = expect(client.emit(hub(), { + methodName: 'missing', requestTimeout: 10, + })).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + await vi.advanceTimersByTimeAsync(10) + await response + expect(vi.getTimerCount()).toBe(0) + }) + + it('keeps the missing-handler error when heartbeat wait expires before the deadline', async () => { + const client = hub({ heartbeatTimeout: 10 }) + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + const response = expect(client.emit(hub(), { + methodName: 'missing', requestTimeout: 100, signal: controller.signal, + })).rejects.toMatchObject({ code: EErrorCode.METHOD_NOT_FOUND }) + await vi.advanceTimersByTimeAsync(10) + await response + expect(remove).toHaveBeenCalledOnce() + expect(vi.getTimerCount()).toBe(0) + }) + + it('supports a hub deadline, per-call override, and explicitly disabling it with 0', async () => { + const client = hub({ requestTimeout: 10 }) + const server = hub() + let finish!: (value: string) => void + server.on(client, 'slow', () => new Promise((resolve) => { finish = resolve })) + const defaultTimeout = expect(client.emit(server, 'slow')).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + await vi.advanceTimersByTimeAsync(10) + await defaultTimeout + const override = expect(client.emit(server, { methodName: 'slow', requestTimeout: 30 })).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + await vi.advanceTimersByTimeAsync(10) + expect(client.pendingCount).toBe(1) + await vi.advanceTimersByTimeAsync(20) + await override + const disabled = client.emit(server, { methodName: 'slow', requestTimeout: 0 }) + await vi.advanceTimersByTimeAsync(1000) + expect(client.pendingCount).toBe(1) + expect(vi.getTimerCount()).toBe(0) + finish('no deadline') + await expect(disabled).resolves.toBe('no deadline') + }) + + it('preserves frozen options, strips local controls from the wire, and removes listeners on success', async () => { + const client = hub() + const server = hub() + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + const options = Object.freeze({ methodName: 'echo', signal: controller.signal, requestTimeout: 100, to: server.instanceID }) + server.on(client, 'echo', (value: number) => value) + await expect(client.emit(server, options, 42)).resolves.toBe(42) + expect(options.signal).toBe(controller.signal) + expect(client.sent[0]).not.toHaveProperty('signal') + expect(client.sent[0]).not.toHaveProperty('requestTimeout') + expect(remove).toHaveBeenCalledOnce() + controller.abort() + expect(vi.getTimerCount()).toBe(0) + }) + + it.each(['send failure', 'destroy'])('cleans up all controls on %s', async (scenario) => { + const client = hub() + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + client.failSend = scenario === 'send failure' + const response = expect(client.emit(hub(), { + methodName: 'missing', signal: controller.signal, requestTimeout: 100, + })).rejects.toMatchObject({ code: scenario === 'destroy' ? EErrorCode.UNKNOWN : EErrorCode.INVALID_MESSAGE }) + if (scenario === 'destroy') client.destroy() + await response + expect(remove).toHaveBeenCalledOnce() + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('handles abort during listener registration before anything is sent', async () => { + const client = hub() + const controller = new AbortController() + const add = controller.signal.addEventListener.bind(controller.signal) + vi.spyOn(controller.signal, 'addEventListener').mockImplementation((...args) => { + controller.abort() + add(...args) + }) + const remove = vi.spyOn(controller.signal, 'removeEventListener') + await expect(client.emit(hub(), { + methodName: 'missing', signal: controller.signal, requestTimeout: 100, + })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(client.sent).toHaveLength(0) + expect(remove).toHaveBeenCalledOnce() + expect(vi.getTimerCount()).toBe(0) + }) + + it('cleans up when abort listener registration fails', async () => { + const client = hub() + const controller = new AbortController() + vi.spyOn(controller.signal, 'addEventListener').mockImplementation(() => { throw new Error('registration failed') }) + await expect(client.emit(hub(), { + methodName: 'missing', signal: controller.signal, requestTimeout: 100, + })).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(client.sent).toHaveLength(0) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it.each([-1, NaN, Infinity, 2147483648])('rejects invalid deadline %s without pending state', async (requestTimeout) => { + expect(() => hub({ requestTimeout })).toThrow(RangeError) + const client = hub() + await expect(client.emit(hub(), { methodName: 'missing', requestTimeout })).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(client.sent).toHaveLength(0) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('rejects invalid signals without creating a pending request', async () => { + const client = hub() + await expect(client.emit(hub(), { methodName: 'missing', signal: {} as AbortSignal })).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(client.pendingCount).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) +}) diff --git a/packages/duplex-message/test/abstract/readiness.tn.ts b/packages/duplex-message/test/abstract/readiness.tn.ts new file mode 100644 index 0000000..4e727f6 --- /dev/null +++ b/packages/duplex-message/test/abstract/readiness.tn.ts @@ -0,0 +1,95 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { EErrorCode, type IMethodNameConfig } from '../../src/abstract' +import { READY_METHOD, waitForPeer } from '../../src/readiness' +import { createRpcClient } from '../../src/rpc-client' + +beforeEach(() => vi.useFakeTimers()) +afterEach(() => vi.useRealTimers()) + +describe('peer readiness', () => { + it('probes only the ready endpoint, then releases timers and signal listeners', async () => { + const controller = new AbortController() + const remove = vi.spyOn(controller.signal, 'removeEventListener') + const emit = vi.fn().mockResolvedValue(true) + await waitForPeer(emit, { signal: controller.signal }) + expect(emit).toHaveBeenCalledWith(expect.objectContaining({ methodName: READY_METHOD })) + expect(remove).toHaveBeenCalledOnce() + expect(vi.getTimerCount()).toBe(0) + }) + + it('retries missing handlers, probe timeouts and not-ready responses', async () => { + const emit = vi.fn() + .mockRejectedValueOnce({ code: EErrorCode.METHOD_NOT_FOUND }) + .mockRejectedValueOnce({ code: EErrorCode.REQUEST_TIMEOUT }) + .mockResolvedValueOnce(false) + .mockResolvedValue(true) + const ready = waitForPeer(emit, { interval: 10, timeout: 100 }) + await vi.advanceTimersByTimeAsync(30) + await ready + expect(emit).toHaveBeenCalledTimes(4) + expect(vi.getTimerCount()).toBe(0) + }) + + it('enforces a total deadline even when an adapter never resolves', async () => { + const emit = vi.fn((_method: IMethodNameConfig) => new Promise(() => {})) + const ready = expect(waitForPeer(emit, { timeout: 20 })).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + await vi.advanceTimersByTimeAsync(20) + await ready + expect(emit.mock.calls[0][0].signal?.aborted).toBe(true) + expect(vi.getTimerCount()).toBe(0) + }) + + it.each(['probe', 'pause'])('cancels during the %s and releases timers', async (phase) => { + const controller = new AbortController() + const emit = phase === 'probe' ? vi.fn(() => new Promise(() => {})) : vi.fn().mockResolvedValue(false) + const ready = expect(waitForPeer(emit, { signal: controller.signal })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + await vi.advanceTimersByTimeAsync(0) + controller.abort() + await ready + await vi.advanceTimersByTimeAsync(1000) + expect(emit).toHaveBeenCalledOnce() + expect(vi.getTimerCount()).toBe(0) + }) + + it('does not probe an already cancelled wait', async () => { + const controller = new AbortController() + controller.abort() + const emit = vi.fn() + await expect(waitForPeer(emit, { signal: controller.signal })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(emit).not.toHaveBeenCalled() + expect(vi.getTimerCount()).toBe(0) + }) + + it('propagates business errors without retrying', async () => { + const error = { code: EErrorCode.HANDLER_EXEC_ERROR, message: 'initialization failed' } + const emit = vi.fn().mockRejectedValue(error) + await expect(waitForPeer(emit)).rejects.toBe(error) + expect(emit).toHaveBeenCalledOnce() + expect(vi.getTimerCount()).toBe(0) + }) + + it('rejects malformed ready responses', async () => { + await expect(waitForPeer(vi.fn().mockResolvedValue('ready'))).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(vi.getTimerCount()).toBe(0) + }) + + it.each([0, -1, NaN, Infinity, 2147483648])('rejects invalid timeout/interval %s', async value => { + const emit = vi.fn() + await expect(waitForPeer(emit, { timeout: value })).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + await expect(waitForPeer(emit, { interval: value })).rejects.toMatchObject({ code: EErrorCode.INVALID_MESSAGE }) + expect(emit).not.toHaveBeenCalled() + expect(vi.getTimerCount()).toBe(0) + }) +}) + +describe('typed RPC client', () => { + it('preserves configuration and arguments while forwarding results and errors', async () => { + interface Api { add(a: number, b: number): number } + const emit = vi.fn().mockResolvedValueOnce(3).mockRejectedValueOnce(new Error('failed')) + const rpc = createRpcClient(emit) + const config = Object.freeze({ methodName: 'add' as const, requestTimeout: 100 }) + await expect(rpc.call(config, 1, 2)).resolves.toBe(3) + expect(emit).toHaveBeenCalledWith(config, 1, 2) + await expect(rpc.call('add', 2, 3)).rejects.toThrow('failed') + }) +}) diff --git a/packages/duplex-message/test/broadcast/index.tb.ts b/packages/duplex-message/test/broadcast/index.tb.ts index 88241a3..82a50fa 100644 --- a/packages/duplex-message/test/broadcast/index.tb.ts +++ b/packages/duplex-message/test/broadcast/index.tb.ts @@ -1,3 +1,4 @@ +import { track } from '../resources' import { expect, describe, it, vi } from 'vitest'; import { setConfig } from 'src/abstract'; @@ -9,14 +10,14 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); describe('broadcast', () => { it('normal usage', async (ctx) => { - const hub2 = new BroadWorker() - const hub = new BroadcastMessageHub(); + const hub2 = track(new BroadWorker()) + const hub = track(new BroadcastMessageHub()); await wait(500); const res = await hub.emit('greet', 'Saiya'); expect(res).toBe('Saiya'); // @ts-expect-error for test expect(hub.isDestroyed).toBe(false); - setConfig({ debug: true }) + setConfig() hub.on('greet', async (msg: string) => { return msg }) @@ -26,13 +27,13 @@ describe('broadcast', () => { // @ts-expect-error for test expect(hub.isDestroyed).toBe(true); - const shared = BroadcastMessageHub.shared; - const shared2 = BroadcastMessageHub.shared; + const shared = track(BroadcastMessageHub.shared); + const shared2 = track(BroadcastMessageHub.shared); expect(shared).toBe(shared2); }); it('edge case 1', async () => { - const hub = new BroadcastMessageHub() + const hub = track(new BroadcastMessageHub()) hub.on(function (mth, data) { return mth === 'greet' ? data : 'error' }) @@ -54,7 +55,7 @@ describe('broadcast', () => { }) }) it('edge case 2', async () => { - const hub = new BroadcastMessageHub() + const hub = track(new BroadcastMessageHub()) // @ts-expect-error for test hub._eventHandlerMap = [[hub.instanceID, { greet: null }]] hub.off('greet') diff --git a/packages/duplex-message/test/broadcast/none-win.tn.ts b/packages/duplex-message/test/broadcast/none-win.tn.ts index 8063262..a2bce6e 100644 --- a/packages/duplex-message/test/broadcast/none-win.tn.ts +++ b/packages/duplex-message/test/broadcast/none-win.tn.ts @@ -1,7 +1,7 @@ +import { track } from '../resources' import { expect, it, describe } from 'vitest'; import { BroadcastMessageHub } from 'src/broadcast-message'; -const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); describe('broadcast in node', () => { @@ -10,18 +10,17 @@ describe('broadcast in node', () => { const OldBroadcastChannel = globalThis.BroadcastChannel; // @ts-expect-error fix type error globalThis.BroadcastChannel = undefined; - expect(() => new BroadcastMessageHub()).toThrowError(); + expect(() => track(new BroadcastMessageHub())).toThrowError(); expect(() => BroadcastMessageHub.shared).toThrowError(); globalThis.BroadcastChannel = OldBroadcastChannel; }); it('normal usage', async (ctx) => { - const hub2 = new BroadcastMessageHub + const hub2 = track(new BroadcastMessageHub()) hub2.on('greet', async (msg: string) => { return msg }) - const hub = new BroadcastMessageHub(); - await wait(500); + const hub = track(new BroadcastMessageHub()); const res = await hub.emit('greet', 'Saiya'); expect(res).toBe('Saiya'); // @ts-expect-error for test @@ -35,33 +34,32 @@ describe('broadcast in node', () => { // @ts-expect-error for test expect(hub.isDestroyed).toBe(true); - const shared = BroadcastMessageHub.shared; - const shared2 = BroadcastMessageHub.shared; + const shared = track(BroadcastMessageHub.shared); + const shared2 = track(BroadcastMessageHub.shared); expect(shared).toBe(shared2); }); it('multi args', async (ctx) => { - const hub2 = new BroadcastMessageHub + const hub2 = track(new BroadcastMessageHub()) hub2.on('greet33', async (a1: number, a2: number) => { return a1 + a2 }) - const hub = new BroadcastMessageHub(); - await wait(500); + const hub = track(new BroadcastMessageHub()); const res = await hub.emit('greet33', 1, 23); expect(res).toBe(24); }); it('test for edge case 1', async () => { - const hub = new BroadcastMessageHub() - expect(() => hub.emit('hello', globalThis)).rejects.toThrowError() + const hub = track(new BroadcastMessageHub()) + await expect(() => hub.emit('hello', globalThis)).rejects.toThrowError() - const hub2 = new BroadcastMessageHub() + const hub2 = track(new BroadcastMessageHub()) hub2.on(console.log) hub2.on('greet2', async (msg: string) => { throw new Error("greet error in hub2"); }) - expect(hub.emit('greet2', 'Saiya')).rejects.toThrowError(); + await expect(hub.emit('greet2', 'Saiya')).rejects.toThrowError(); }) }) diff --git a/packages/duplex-message/test/controls/index.tb.ts b/packages/duplex-message/test/controls/index.tb.ts new file mode 100644 index 0000000..dcfe180 --- /dev/null +++ b/packages/duplex-message/test/controls/index.tb.ts @@ -0,0 +1,68 @@ +import { describe, expect, it, vi } from 'vitest' +import { EErrorCode } from '../../src/abstract' +import { BroadcastMessageHub } from '../../src/broadcast-message' +import { PageScriptMessageHub } from '../../src/page-script-message' +import { StorageMessageHub } from '../../src/storage-message' +import { track } from '../resources' + +type Transport = 'page-script' | 'broadcast' | 'storage' + +async function createClient(transport: Transport) { + const channel = `controls-${crypto.randomUUID()}` + if (transport === 'storage') { + const frame = track(document.createElement('iframe')) + frame.srcdoc = `` + const loaded = new Promise((resolve) => { frame.onload = () => resolve() }) + document.body.appendChild(frame) + await loaded + return track(new StorageMessageHub({ keyPrefix: channel })) + } + const options = { channelName: channel, customEventName: channel } + const Hub = transport === 'broadcast' ? BroadcastMessageHub : PageScriptMessageHub + const client = track(new Hub(options)) + const server = track(new Hub(options)) + server.on('controls-progress', (options: { onprogress: (value: string) => void }) => { + options.onprogress('--message-hub-to-be-continued--') + return 'done' + }) + server.on('controls-slow', (options: { onprogress: (value: string) => void }) => { + options.onprogress('started') + return new Promise(() => {}) + }) + return client +} + +describe.each(['page-script', 'broadcast', 'storage'])('%s request controls', (transport) => { + it('delivers reserved-string progress with local controls and preserves success after abort', async () => { + const client = await createClient(transport) + const controller = new AbortController() + const onprogress = vi.fn() + await expect(client.emit({ + methodName: 'controls-progress', signal: controller.signal, requestTimeout: 1000, + }, { onprogress })).resolves.toBe('done') + controller.abort() + expect(onprogress.mock.calls).toEqual([['--message-hub-to-be-continued--']]) + await expect(client.emit('controls-progress', { onprogress })).resolves.toBe('done') + }) + + it('cancels a request after receiving progress without serializing its AbortSignal', async () => { + const client = await createClient(transport) + const controller = new AbortController() + const onprogress = vi.fn() + const response = expect(client.emit({ + methodName: 'controls-slow', signal: controller.signal, requestTimeout: 2000, + }, { onprogress })).rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + await vi.waitFor(() => expect(onprogress).toHaveBeenCalledWith('started')) + controller.abort() + await response + }) + + it('enforces a request deadline even after the heartbeat', async () => { + const client = await createClient(transport) + const onprogress = vi.fn() + await expect(client.emit({ + methodName: 'controls-slow', requestTimeout: 500, + }, { onprogress })).rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + expect(onprogress).toHaveBeenCalledWith('started') + }) +}) diff --git a/packages/duplex-message/test/cross-origin-server.ts b/packages/duplex-message/test/cross-origin-server.ts new file mode 100644 index 0000000..523cae4 --- /dev/null +++ b/packages/duplex-message/test/cross-origin-server.ts @@ -0,0 +1,18 @@ +import { createServer } from 'vite' +import type { TestProject } from 'vitest/node' + +declare module 'vitest' { + export interface ProvidedContext { frameOrigin: string } +} + +export default async function setup(project: TestProject) { + const server = await createServer({ + configFile: false, + server: { host: '127.0.0.1', port: 0 }, + }) + await server.listen() + const address = server.httpServer!.address() + if (!address || typeof address === 'string') throw new Error('fixture server did not bind a port') + project.provide('frameOrigin', `http://127.0.0.1:${address.port}`) + return () => server.close() +} diff --git a/packages/duplex-message/test/pagescript/index.tb.ts b/packages/duplex-message/test/pagescript/index.tb.ts index d210b24..e3cc235 100644 --- a/packages/duplex-message/test/pagescript/index.tb.ts +++ b/packages/duplex-message/test/pagescript/index.tb.ts @@ -1,32 +1,43 @@ +import { track } from '../resources' import { expect, describe, it, vi } from 'vitest'; import { PageScriptMessageHub } from 'src/page-script-message'; describe('page script', () => { + it('creates a usable shared instance after destroy', async () => { + const original = track(PageScriptMessageHub.shared) + original.destroy() + const replacement = track(PageScriptMessageHub.shared) + expect(replacement).not.toBe(original) + replacement.on('echo', (value: string) => value) + const client = track(new PageScriptMessageHub()) + await expect(client.emit('echo', 'ready')).resolves.toBe('ready') + }) + it('normal usage', async () => { - const p1 = new PageScriptMessageHub(); - const p2 = new PageScriptMessageHub(); + const p1 = track(new PageScriptMessageHub()); + const p2 = track(new PageScriptMessageHub()); const cb = vi.fn(); p1.on('hello', cb) - p2.emit('hello', 'world'); - expect(cb).toBeCalledWith('world'); + await p2.emit('hello', 'world'); + expect(cb).toHaveBeenCalledWith('world'); p1.off('hello'); - expect(p2.emit('hello', 'world')).rejects.toThrowError(); + await expect(p2.emit('hello', 'world')).rejects.toThrowError(); p1.on('hello', cb); p1.destroy(); p1.destroy(); - expect(p2.emit('hello', 'world')).rejects.toThrowError(); + await expect(p2.emit('hello', 'world')).rejects.toThrowError(); }); it('use shared instance', async () => { - const shared = PageScriptMessageHub.shared; + const shared = track(PageScriptMessageHub.shared); const cb = vi.fn(); - const p2 = new PageScriptMessageHub(); + const p2 = track(new PageScriptMessageHub()); shared.on('hello', cb) - p2.emit('hello', 'world'); - expect(cb).toBeCalledWith('world'); - const shared2 = PageScriptMessageHub.shared; + await p2.emit('hello', 'world'); + expect(cb).toHaveBeenCalledWith('world'); + const shared2 = track(PageScriptMessageHub.shared); shared2.off('hello'); - expect(p2.emit('hello', 'world')).rejects.toThrowError(); + await expect(p2.emit('hello', 'world')).rejects.toThrowError(); }) -}); \ No newline at end of file +}); diff --git a/packages/duplex-message/test/pagescript/none-win.tn.ts b/packages/duplex-message/test/pagescript/none-win.tn.ts index 6c40264..9adb824 100644 --- a/packages/duplex-message/test/pagescript/none-win.tn.ts +++ b/packages/duplex-message/test/pagescript/none-win.tn.ts @@ -1,7 +1,8 @@ +import { track } from '../resources' import { expect, it } from 'vitest'; import { PageScriptMessageHub } from 'src/page-script-message'; it('should throw in non window env', async () => { - expect(() => new PageScriptMessageHub()).toThrowError(); + expect(() => track(new PageScriptMessageHub())).toThrowError(); expect(() => PageScriptMessageHub.shared).toThrowError(); }); diff --git a/packages/duplex-message/test/post-message/frame-proxy/index.tb.ts b/packages/duplex-message/test/post-message/frame-proxy/index.tb.ts index a308df5..ac03552 100644 --- a/packages/duplex-message/test/post-message/frame-proxy/index.tb.ts +++ b/packages/duplex-message/test/post-message/frame-proxy/index.tb.ts @@ -1,3 +1,4 @@ +import { track } from '../../resources' import { PostMessageHub } from 'src/post-message'; import { describe, it, expect } from 'vitest'; @@ -7,9 +8,9 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const frames: HTMLIFrameElement[] = []; const createFrame = (keepWin?: boolean) => { - keepWin || frames.forEach((frame) => frame.remove()); + if (!keepWin) frames.forEach((frame) => frame.remove()); frames.length = 0; - const frame = document.createElement('iframe'); + const frame = track(document.createElement('iframe')); frame.srcdoc = ` `; @@ -24,7 +25,7 @@ describe('PostMessage for proxy', () => { await wait(1000); const frameWindow = frame.contentWindow as Window - const hub = new PostMessageHub() + const hub = track(new PostMessageHub()) const msg = await hub.emit(frameWindow, 'greet', 'hello') expect(msg).toBe('hello') @@ -40,7 +41,7 @@ describe('PostMessage for proxy', () => { const frameWindow2 = frame2.contentWindow as Window hub.createProxy(frameWindow, frameWindow2) - hub.emit(frameWindow, 'start-proxy') + await hub.emit(frameWindow, 'start-proxy') await wait(1000); hub.stopProxy(frameWindow) // @ts-expect-error for test @@ -48,9 +49,7 @@ describe('PostMessage for proxy', () => { expect(() => hub.createProxy(frameWindow, frameWindow2)).toThrowError() hub.destroy() - // @ts-expect-error for test - window.parent = null - expect(hub.emit(window, 'greet', 'hello')).rejects.toThrowError() + expect(() => hub.emit(window, 'greet', 'hello')).toThrow('destroyed') }) }) diff --git a/packages/duplex-message/test/post-message/frame/index.tb.ts b/packages/duplex-message/test/post-message/frame/index.tb.ts index 3779242..b3d8b00 100644 --- a/packages/duplex-message/test/post-message/frame/index.tb.ts +++ b/packages/duplex-message/test/post-message/frame/index.tb.ts @@ -1,3 +1,4 @@ +import { track } from '../../resources' import { PostMessageHub } from 'src/post-message'; import { describe, it, expect } from 'vitest'; @@ -7,9 +8,9 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const frames: HTMLIFrameElement[] = []; const createFrame = (keepWin?: boolean) => { - keepWin || frames.forEach((frame) => frame.remove()); + if (!keepWin) frames.forEach((frame) => frame.remove()); frames.length = 0; - const frame = document.createElement('iframe'); + const frame = track(document.createElement('iframe')); frame.srcdoc = ` `; @@ -24,7 +25,7 @@ describe('PostMessage for iframe', () => { await wait(1000); const frameWindow = frame.contentWindow as Window - const hub = new PostMessageHub({ heartbeatTimeout: 1000 }); + const hub = track(new PostMessageHub({ heartbeatTimeout: 1000 })); const msg = await hub.emit(frameWindow, { methodName: 'greet', targetOrigin: '*' }, 'hello') expect(msg).toBe('hello') @@ -34,7 +35,7 @@ describe('PostMessage for iframe', () => { expect(resp).toBe('Saiya') - expect(() => hub.emit(frameWindow, { methodName: 'greet', targetOrigin: 'https://www.google.com/' }, 'hello')).rejects.toThrowError() + await expect(() => hub.emit(frameWindow, { methodName: 'greet', targetOrigin: 'https://www.google.com/' }, 'hello')).rejects.toThrowError() }) it('with instanceID', async () => { @@ -42,9 +43,9 @@ describe('PostMessage for iframe', () => { await wait(1000); const frameWindow = frame.contentWindow as Window - const hub = new PostMessageHub({ heartbeatTimeout: 1000 }); + const hub = track(new PostMessageHub({ heartbeatTimeout: 1000 })); - expect(hub.emit(frameWindow, { methodName: 'greet', to: 'ab', targetOrigin: '*' }, 'hello')).rejects.toThrowError() + await expect(hub.emit(frameWindow, { methodName: 'greet', to: 'ab', targetOrigin: '*' }, 'hello')).rejects.toThrowError() const msg = await hub.emit(frameWindow, { methodName: 'greet', targetOrigin: '*' }, 'hello') expect(msg).toBe('hello') const msg2 = await hub.emit(frameWindow, { methodName: 'greet', to: 'iframe', targetOrigin: '*' }, 'hello') diff --git a/packages/duplex-message/test/post-message/security/frame.html b/packages/duplex-message/test/post-message/security/frame.html new file mode 100644 index 0000000..4a1f511 --- /dev/null +++ b/packages/duplex-message/test/post-message/security/frame.html @@ -0,0 +1,2 @@ + + diff --git a/packages/duplex-message/test/post-message/security/frame.ts b/packages/duplex-message/test/post-message/security/frame.ts new file mode 100644 index 0000000..4e859e4 --- /dev/null +++ b/packages/duplex-message/test/post-message/security/frame.ts @@ -0,0 +1,8 @@ +import { PostMessageHub } from '../../../src/post-message' +import { READY_METHOD } from '../../../src/readiness' + +const parentOrigin = new URL(location.href).searchParams.get('parentOrigin')! +const hub = new PostMessageHub({ allowedOrigins: [parentOrigin], targetOrigin: parentOrigin }) +hub.on(parent, 'echo', (value: string) => value) +// Represents asynchronous application initialization after the document loads. +setTimeout(() => hub.on(parent, READY_METHOD, () => true), 200) diff --git a/packages/duplex-message/test/post-message/security/index.tb.ts b/packages/duplex-message/test/post-message/security/index.tb.ts new file mode 100644 index 0000000..994fe36 --- /dev/null +++ b/packages/duplex-message/test/post-message/security/index.tb.ts @@ -0,0 +1,120 @@ +import { describe, expect, inject, it, vi } from 'vitest' +import { EErrorCode } from '../../../src/abstract' +import { PostMessageHub } from '../../../src/post-message' +import { waitForPeer } from '../../../src/readiness' +import { createRpcClient } from '../../../src/rpc-client' +import { track } from '../../resources' +import DemoWorker from '../worker/worker?worker' + +function frame() { + const element = track(document.createElement('iframe')) + const url = new URL('/test/post-message/security/frame.html', inject('frameOrigin')) + url.searchParams.set('parentOrigin', location.origin) + element.src = url.href + document.body.appendChild(element) + return { peer: element.contentWindow!, origin: url.origin } +} + +describe('window origin policy and readiness', () => { + it('connects to a real cross-origin iframe before calling a typed business method', async () => { + const { peer, origin } = frame() + const hub = track(new PostMessageHub({ allowedOrigins: [origin], targetOrigin: origin })) + await waitForPeer(method => hub.emit(peer, method), { timeout: 5000 }) + const rpc = createRpcClient<{ echo(value: string): string }>((method, ...args) => hub.emit(peer, method, ...args)) + await expect(rpc.call('echo', 'trusted')).resolves.toBe('trusted') + }) + + it('ignores requests from disallowed origins before running handlers', async () => { + const hub = track(new PostMessageHub({ allowedOrigins: [location.origin] })) + const handler = vi.fn(() => 'secret') + hub.on(self, 'secret', handler) + window.dispatchEvent(new MessageEvent('message', { + source: window, origin: 'https://untrusted.example', + data: { from: 'remote', to: hub.instanceID, messageID: 1, type: 'request', methodName: 'secret', data: [] }, + })) + await Promise.resolve() + expect(handler).not.toHaveBeenCalled() + }) + + it('ignores responses from a real iframe outside the configured origins', async () => { + const { peer, origin } = frame() + const trusted = track(new PostMessageHub({ allowedOrigins: [origin], targetOrigin: origin })) + await waitForPeer(method => trusted.emit(peer, method)) + const hub = track(new PostMessageHub({ allowedOrigins: ['https://untrusted.example'], targetOrigin: origin })) + await expect(hub.emit(peer, { methodName: 'echo', requestTimeout: 100 }, 'blocked')) + .rejects.toMatchObject({ code: EErrorCode.REQUEST_TIMEOUT }) + }) + + it('pins replies to the accepted request origin', async () => { + const hub = track(new PostMessageHub({ allowedOrigins: [location.origin], targetOrigin: 'https://other.example' })) + const send = vi.spyOn(window, 'postMessage') + hub.on(self, 'echo', () => 'done') + window.dispatchEvent(new MessageEvent('message', { + source: window, origin: location.origin, + data: { from: 'remote', to: hub.instanceID, messageID: 1, type: 'request', methodName: 'echo', data: [] }, + })) + await vi.waitFor(() => expect(send).toHaveBeenCalledWith(expect.objectContaining({ type: 'response' }), location.origin)) + send.mockRestore() + }) + + it('keeps the configured outgoing origin after receiving an accepted message', async () => { + const targetOrigin = 'https://outgoing.example' + const hub = track(new PostMessageHub({ allowedOrigins: [location.origin], targetOrigin })) + const send = vi.spyOn(window, 'postMessage') + hub.on(self, 'echo', () => 'done') + window.dispatchEvent(new MessageEvent('message', { + source: window, origin: location.origin, + data: { from: 'remote', to: hub.instanceID, messageID: 1, type: 'request', methodName: 'echo', data: [] }, + })) + const controller = new AbortController() + const pending = expect(hub.emit(self, { methodName: 'outgoing', signal: controller.signal })) + .rejects.toMatchObject({ code: EErrorCode.REQUEST_ABORTED }) + expect(send).toHaveBeenCalledWith(expect.objectContaining({ type: 'request' }), targetOrigin) + controller.abort() + await pending + send.mockRestore() + }) + + it('keeps Worker communication working with a window origin policy', async () => { + const worker = track(new DemoWorker()) + const hub = track(new PostMessageHub({ allowedOrigins: [] })) + await expect(hub.emit(worker, 'greet', 'hello')).resolves.toBe('hello') + }) + + it('keeps an asynchronous response pinned to its request origin after another accepted origin sends a request', async () => { + const otherOrigin = 'https://other.example' + const hub = track(new PostMessageHub({ allowedOrigins: [location.origin, otherOrigin] })) + const send = vi.spyOn(window, 'postMessage') + let finish!: (value: string) => void + hub.on(self, 'slow', () => new Promise(resolve => { finish = resolve })) + hub.on(self, 'echo', () => 'second') + const request = (origin: string, from: string, messageID: number, methodName: string) => { + window.dispatchEvent(new MessageEvent('message', { + source: window, origin, + data: { from, to: hub.instanceID, messageID, type: 'request', methodName, data: [] }, + })) + } + request(location.origin, 'first', 1, 'slow') + request(otherOrigin, 'second', 2, 'echo') + finish('first result') + await vi.waitFor(() => expect(send).toHaveBeenCalledWith( + expect.objectContaining({ type: 'response', to: 'first', messageID: 1 }), location.origin, + )) + send.mockRestore() + }) + + it('copies caller-owned origin lists and validates options', () => { + const origins = [location.origin] + const hub = track(new PostMessageHub({ allowedOrigins: origins })) + origins.push('https://other.example') + const handler = vi.fn() + hub.on(self, 'echo', handler) + window.dispatchEvent(new MessageEvent('message', { + source: window, origin: 'https://other.example', + data: { from: 'remote', messageID: 1, type: 'request', methodName: 'echo', data: [] }, + })) + expect(handler).not.toHaveBeenCalled() + expect(() => new PostMessageHub({ allowedOrigins: [''] })).toThrow(TypeError) + expect(() => new PostMessageHub({ targetOrigin: '' })).toThrow(TypeError) + }) +}) diff --git a/packages/duplex-message/test/post-message/worker/index.tb.ts b/packages/duplex-message/test/post-message/worker/index.tb.ts index 6d0035a..2cd479a 100644 --- a/packages/duplex-message/test/post-message/worker/index.tb.ts +++ b/packages/duplex-message/test/post-message/worker/index.tb.ts @@ -1,14 +1,133 @@ +import { track } from '../../resources' import { PostMessageHub } from 'src/post-message'; -import { describe, it, expect } from 'vitest'; +import { describe, it, expect, vi } from 'vitest'; import DemoWorker from './worker?worker' const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); +async function createWorker() { + const worker = track(new DemoWorker()) + await new Promise((resolve, reject) => { + const cleanup = () => { + worker.removeEventListener('message', ready) + worker.removeEventListener('error', failed) + } + const ready = (event: MessageEvent) => { + if (event.data?.type !== 'fixture-ready') return + cleanup() + resolve() + } + const failed = (event: ErrorEvent) => { cleanup(); reject(new Error(event.message)) } + worker.addEventListener('message', ready) + worker.addEventListener('error', failed) + }) + return worker +} + describe('PostMessage in worker', () => { + it('does not attach a Worker listener or transfer buffers for pre-aborted/invalid requests', async () => { + class InspectableHub extends PostMessageHub { + get workerCount() { return this._hostedWorkers.length } + } + const worker = await createWorker() + const hub = track(new InspectableHub()) + const controller = new AbortController() + controller.abort() + const buffer = new ArrayBuffer(16) + await expect(hub.emit(worker, { + methodName: 'greet', signal: controller.signal, transfer: [buffer], + }, buffer)).rejects.toMatchObject({ code: 6 }) + expect(buffer.byteLength).toBe(16) + expect(hub.workerCount).toBe(0) + await expect(hub.emit(worker, { methodName: 'greet', requestTimeout: -1 })).rejects.toMatchObject({ code: 4 }) + expect(hub.workerCount).toBe(0) + }) + + it('delivers reserved-string progress across the Worker structured-clone boundary', async () => { + const worker = await createWorker() + const hub = track(new PostMessageHub()) + const onprogress = vi.fn() + const controller = new AbortController() + await expect(hub.emit(worker, { + methodName: 'controls-progress', signal: controller.signal, requestTimeout: 2000, + }, { onprogress })).resolves.toBe('done') + expect(onprogress.mock.calls).toEqual([['--message-hub-to-be-continued--']]) + }) + + it.each(['abort', 'timeout'])('releases the Worker listener on request %s', async (scenario) => { + class InspectableHub extends PostMessageHub { + get workerCount() { return this._hostedWorkers.length } + } + const worker = await createWorker() + const hub = track(new InspectableHub()) + const controller = new AbortController() + const onprogress = vi.fn() + const response = expect(hub.emit(worker, { + methodName: 'controls-slow', signal: controller.signal, requestTimeout: 1000, + }, { onprogress })).rejects.toMatchObject({ code: scenario === 'abort' ? 6 : 7 }) + await vi.waitFor(() => expect(onprogress).toHaveBeenCalledWith('started')) + if (scenario === 'abort') controller.abort() + await response + expect(hub.workerCount).toBe(0) + }) + + it('keeps wildcard handlers listening for later Worker calls and releases them on off', async () => { + class InspectableHub extends PostMessageHub { + get workerCount() { return this._hostedWorkers.length } + } + const worker = await createWorker() + const hub = track(new InspectableHub()) + const handler = vi.fn(() => 'handled') + hub.on('*', 'background', handler) + await expect(hub.emit(worker, 'greet', 'hello')).resolves.toBe('hello') + expect(hub.workerCount).toBe(1) + worker.postMessage({ type: 'trigger-background' }) + await vi.waitFor(() => expect(handler).toHaveBeenCalledWith('ping')) + hub.off('*', 'background') + expect(hub.workerCount).toBe(0) + }) + + it('removes wildcard handlers while retaining pending calls and peer-specific handlers', async () => { + class InspectableHub extends PostMessageHub { + get workerCount() { return this._hostedWorkers.length } + } + const worker = await createWorker() + const hub = track(new InspectableHub()) + hub.on('*', 'background', () => 'handled') + const pending = hub.emit(worker, 'greet', 'hello') + hub.off('*') + expect(hub.workerCount).toBe(1) + await expect(pending).resolves.toBe('hello') + expect(hub.workerCount).toBe(0) + hub.on('*', 'background', () => 'handled') + hub.on(worker, 'specific', () => 'handled') + hub.off('*') + expect(hub.workerCount).toBe(1) + hub.off(worker) + expect(hub.workerCount).toBe(0) + }) + + it('keeps the Worker listener for pending calls after off, then releases it', async () => { + class InspectableHub extends PostMessageHub { + get workerCount() { return this._hostedWorkers.length } + } + const worker = await createWorker() + const hub = track(new InspectableHub()) + hub.on(worker, 'unused', () => {}) + const slow = hub.emit(worker, 'download', { onprogress: () => {} }) + const fast = hub.emit(worker, 'greet', 'hello') + hub.off(worker) + expect(hub.workerCount).toBe(1) + await expect(fast).resolves.toBe('hello') + expect(hub.workerCount).toBe(1) + await expect(slow).resolves.toBe('done with progress') + expect(hub.workerCount).toBe(0) + }) + it('normal usage', async () => { - const worker = new DemoWorker + const worker = await createWorker() - const hub = new PostMessageHub() + const hub = track(new PostMessageHub()) hub.on(worker, 'greet', async (msg: string) => { console.log('on message from worker', msg) }) @@ -18,17 +137,15 @@ describe('PostMessage in worker', () => { worker.terminate() hub.on(worker, 'greet', console.log) hub.destroy() - // @ts-expect-error for test - window.parent = null - expect(hub.emit(window, 'greet', 'hello')).rejects.toThrowError() + expect(() => hub.emit(window, 'greet', 'hello')).toThrow('destroyed') }) it('use shared instance', async () => { - const worker = new DemoWorker + const worker = await createWorker() - const hub = PostMessageHub.shared - const hub2 = PostMessageHub.shared + const hub = track(PostMessageHub.shared) + const hub2 = track(PostMessageHub.shared) expect(hub).toBe(hub2) hub.on(worker, 'greet', async (msg: string) => { console.log('on message from worker', msg) @@ -44,9 +161,9 @@ describe('PostMessage in worker', () => { }) it('test for edge case 1', async () => { - const worker = new DemoWorker + const worker = await createWorker() - const hub = new PostMessageHub() + const hub = track(new PostMessageHub()) hub.on(worker, 'greet', async (msg: string) => { console.log('on message from worker', msg) }) @@ -77,8 +194,8 @@ describe('PostMessage in worker', () => { }) it('test for edge case 2', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.stopProxy(worker) hub.on(worker, 'greet', async (msg: string) => { throw new Error("test error in request"); @@ -97,8 +214,8 @@ describe('PostMessage in worker', () => { }) it('test for edge case 3', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on(worker, 'greet', async (msg: string) => { return 'hello' }) @@ -112,8 +229,8 @@ describe('PostMessage in worker', () => { it('test for edge case 4', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on(worker, 'greet', async (msg: string) => { console.log('demo message', msg) }) @@ -126,14 +243,14 @@ describe('PostMessage in worker', () => { }) it('test for edge case 5', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on(worker, 'some-method', console.log) - expect(hub.emit(worker, 'inter-star', 'hello')).rejects.toThrowError() + await expect(hub.emit(worker, 'inter-star', 'hello')).rejects.toThrowError() }) it('test for edge case 6', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on(worker, 'greet', async (msg: string) => { throw { stack: 'custom error stack', @@ -145,17 +262,17 @@ describe('PostMessage in worker', () => { }) it('test when no callback', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const msg = hub.emit(worker, 'greetxxxx', 'hello') - expect(msg).rejects.toThrowError() + await expect(msg).rejects.toThrowError() const msg1 = await hub.emit(worker, 'test-for-inter-call', 'hello') expect(msg1).toBe('not found') }) it('invalid callback', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) // @ts-expect-error for test hub.on(worker, 'greet', 'invalid') const msg = await hub.emit(worker, 'inter-greet', 'hello') @@ -167,8 +284,8 @@ describe('PostMessage in worker', () => { describe('Dedicated message hub', () => { it('normal usage', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(worker) const msg = await workerMessage.emit('greet', 'hello') @@ -184,8 +301,8 @@ describe('Dedicated message hub', () => { }) it('should throw when peer not set', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub() expect(() => workerMessage.emit('greet', 'hello')).toThrowError() expect(() => workerMessage.on('greet', console.log)).toThrowError() @@ -197,10 +314,10 @@ describe('Dedicated message hub', () => { }) it('should be silent when peer not set', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(undefined, true) - expect(() => workerMessage.emit('greet', 'hello')).rejects.toThrowError() + await expect(() => workerMessage.emit('greet', 'hello')).rejects.toThrowError() workerMessage.on('greet', console.log) workerMessage.off('greet') @@ -209,8 +326,8 @@ describe('Dedicated message hub', () => { }) it('should off correctly', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(worker) workerMessage.on('greet', console.log) workerMessage.on('greets', console.log) @@ -221,19 +338,23 @@ describe('Dedicated message hub', () => { }) it('support for transfer params', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(worker) const data = new ArrayBuffer(16) + new Uint8Array(data)[0] = 42 const resp = await workerMessage.emit({ methodName: 'greet', transfer: [data] }, data) - expect(resp).toStrictEqual(data) + expect(data.byteLength).toBe(0) + expect(resp).toBeInstanceOf(ArrayBuffer) + expect((resp as ArrayBuffer).byteLength).toBe(16) + expect(new Uint8Array(resp as ArrayBuffer)[0]).toBe(42) }) }) describe('check for progress', () => { it('download', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(worker) let progressCount = 0 const msg = await workerMessage.emit('download', { @@ -247,8 +368,8 @@ describe('check for progress', () => { }) it('progress in parent', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) const workerMessage = hub.createDedicatedMessageHub(worker) let progressCount = 0 workerMessage.on('download', async (params: {url: string, onprogress: (n: number) => void}) => { @@ -272,8 +393,8 @@ describe('check for progress', () => { describe('postMessage general function', () => { it('general message event', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on(worker, console.log) hub.on(worker, 'greet', async (msg: string) => { @@ -291,8 +412,8 @@ describe('check for progress', () => { describe('postMessage * all message', () => { it('general message callback', async () => { - const worker = new DemoWorker - const hub = new PostMessageHub() + const worker = await createWorker() + const hub = track(new PostMessageHub()) hub.on('*', function (methodName, arg1) { return `${methodName}-${arg1}` }) diff --git a/packages/duplex-message/test/post-message/worker/worker.ts b/packages/duplex-message/test/post-message/worker/worker.ts index b9fe089..6b69524 100644 --- a/packages/duplex-message/test/post-message/worker/worker.ts +++ b/packages/duplex-message/test/post-message/worker/worker.ts @@ -1,6 +1,20 @@ import { PostMessageHub } from 'src/post-message'; const hub = new PostMessageHub(); +hub.on(self, 'controls-progress', (options: { onprogress: (value: string) => void }) => { + options.onprogress('--message-hub-to-be-continued--') + return 'done' +}) +hub.on(self, 'controls-slow', (options: { onprogress: (value: string) => void }) => { + options.onprogress('started') + return new Promise(() => {}) +}) +// Trigger a peer-initiated RPC after the caller's original request has settled. +self.addEventListener('message', (event) => { + if (event.data?.type === 'trigger-background') { + void hub.emit(self, 'background', 'ping').catch(() => {}) + } +}) console.log('hooo worker') hub.on(self, 'greet', async (msg: string) => { return msg @@ -57,4 +71,7 @@ hub.on(self, 'inter-greet', async (req) => { hub.on(self, 'inter-star', async (req) => { const msg = await hub.emit(self, 'inter', 'hello') return msg -}) \ No newline at end of file +}) + +// Keep cold module loading separate from RPC acknowledgement deadlines in tests. +self.postMessage({ type: 'fixture-ready' }) diff --git a/packages/duplex-message/test/resources.ts b/packages/duplex-message/test/resources.ts new file mode 100644 index 0000000..b11f1e6 --- /dev/null +++ b/packages/duplex-message/test/resources.ts @@ -0,0 +1,18 @@ +import { afterEach } from 'vitest' + +type Resource = { destroy(): void } | { terminate(): void } | { remove(): void } +const resources = new Set() + +export function track(resource: T): T { + resources.add(resource) + return resource +} + +afterEach(() => { + for (const resource of resources) { + if ('destroy' in resource) resource.destroy() + else if ('terminate' in resource) resource.terminate() + else resource.remove() + } + resources.clear() +}) diff --git a/packages/duplex-message/test/storage/frame-source.ts b/packages/duplex-message/test/storage/frame-source.ts index dd0ec4f..9d1a2c0 100644 --- a/packages/duplex-message/test/storage/frame-source.ts +++ b/packages/duplex-message/test/storage/frame-source.ts @@ -4,13 +4,30 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); console.log('frame aaa storage', location.href) -const hub = new StorageMessageHub; +const keyPrefix = new URL(import.meta.url).searchParams.get('keyPrefix') +const hub = new StorageMessageHub(keyPrefix === null ? undefined : { keyPrefix }); + +hub.on('controls-progress', (options: { onprogress: (value: string) => void }) => { + options.onprogress('--message-hub-to-be-continued--') + return 'done' +}) +hub.on('controls-slow', (options: { onprogress: (value: string) => void }) => { + options.onprogress('started') + return new Promise(() => {}) +}) hub.on('greet', async (msg: string) => { await wait(200) return msg }) +hub.on('repeat-progress', (options: { onprogress: (value: number) => void }) => { + options.onprogress(7) + options.onprogress(7) + options.onprogress(7) + return 'done' +}) + hub.on('download', async (params: {url: string, onprogress: (n: number) => void}) => { return new Promise((resolve, reject) => { diff --git a/packages/duplex-message/test/storage/index.tb.ts b/packages/duplex-message/test/storage/index.tb.ts index 337b06e..5ee281a 100644 --- a/packages/duplex-message/test/storage/index.tb.ts +++ b/packages/duplex-message/test/storage/index.tb.ts @@ -1,3 +1,4 @@ +import { track } from '../resources' import { expect, describe, it, vi } from 'vitest'; import { setConfig } from 'src/abstract'; @@ -8,9 +9,9 @@ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const frames: HTMLIFrameElement[] = []; const createFrame = (keepWin?: boolean) => { - keepWin || frames.forEach((frame) => frame.remove()); + if (!keepWin) frames.forEach((frame) => frame.remove()); frames.length = 0; - const frame = document.createElement('iframe'); + const frame = track(document.createElement('iframe')); frame.srcdoc = ` `; @@ -21,15 +22,30 @@ const createFrame = (keepWin?: boolean) => { describe('Storage', () => { + it('delivers identical consecutive progress updates and immediately removes message keys', async () => { + const prefix = `storage-regression-${Date.now()}` + // The fixture uses the default prefix; the isolated sender check exercises cleanup separately. + const hub = track(new StorageMessageHub()) + createFrame() + await wait(1000) + const onprogress = vi.fn() + await expect(hub.emit('repeat-progress', { onprogress })).resolves.toBe('done') + expect(onprogress.mock.calls).toEqual([[7], [7], [7]]) + const isolated = track(new StorageMessageHub({ keyPrefix: prefix, heartbeatTimeout: 10 })) + const missing = expect(isolated.emit('missing')).rejects.toMatchObject({ code: 3 }) + expect(Object.keys(localStorage).some((key) => key.startsWith(prefix))).toBe(false) + await missing + }) + it('normal usage', async (ctx) => { - const hub = new StorageMessageHub(); + const hub = track(new StorageMessageHub()); const frame = createFrame(); await wait(1000); const res = await hub.emit('greet', 'Saiya'); expect(res).toBe('Saiya'); // @ts-expect-error for test expect(hub.isDestroyed).toBe(false); - setConfig({ debug: true }) + setConfig() hub.on('greet', async (msg: string) => { return msg }) @@ -39,19 +55,19 @@ describe('Storage', () => { // @ts-expect-error for test expect(hub.isDestroyed).toBe(true); - const shared = StorageMessageHub.shared; - const shared2 = StorageMessageHub.shared; + const shared = track(StorageMessageHub.shared); + const shared2 = track(StorageMessageHub.shared); expect(shared).toBe(shared2); }); it('exception', async (ctx) => { - const hub = new StorageMessageHub(); + const hub = track(new StorageMessageHub()); const frame = createFrame(); await wait(1000); - expect(() => hub.emit('hello', window)).rejects.toThrowError(); + await expect(() => hub.emit('hello', window)).rejects.toThrowError(); - hub.emit('test-for-exception'); + await hub.emit('test-for-exception'); await wait(1000); localStorage.setItem(``, 'test 2323'); @@ -67,7 +83,7 @@ describe('Storage', () => { }) it('multi frame', async (ctx) => { - const hub = new StorageMessageHub(); + const hub = track(new StorageMessageHub()); const frame = createFrame(); const frame2 = createFrame(true); await wait(2000); @@ -75,7 +91,7 @@ describe('Storage', () => { expect(res).toBe('Saiya'); // @ts-expect-error for test expect(hub.isDestroyed).toBe(false); - setConfig({ debug: true }) + setConfig() hub.on('greet', async (msg: string) => { return msg }) @@ -85,8 +101,8 @@ describe('Storage', () => { // @ts-expect-error for test expect(hub.isDestroyed).toBe(true); - const shared = StorageMessageHub.shared; - const shared2 = StorageMessageHub.shared; + const shared = track(StorageMessageHub.shared); + const shared2 = track(StorageMessageHub.shared); expect(shared).toBe(shared2); }) -}); \ No newline at end of file +}); diff --git a/packages/duplex-message/test/storage/none-win.tn.ts b/packages/duplex-message/test/storage/none-win.tn.ts index 7d30d15..31f9e57 100644 --- a/packages/duplex-message/test/storage/none-win.tn.ts +++ b/packages/duplex-message/test/storage/none-win.tn.ts @@ -1,10 +1,9 @@ +import { track } from '../resources' import { expect, it } from 'vitest'; import { StorageMessageHub } from 'src/storage-message'; it('should throw when Storage not supported', async () => { - // @ts-expect-error fix type error - globalThis.BroadcastChannel = undefined; - expect(() => new StorageMessageHub()).toThrowError(); + expect(() => track(new StorageMessageHub())).toThrowError(); expect(() => StorageMessageHub.shared).toThrowError(); const o = { a: { b: { c: 1 } }, diff --git a/packages/duplex-message/tsconfig.build.json b/packages/duplex-message/tsconfig.build.json new file mode 100644 index 0000000..75a0c77 --- /dev/null +++ b/packages/duplex-message/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "emitDeclarationOnly": true + }, + "include": [ + "src" + ] +} diff --git a/packages/duplex-message/tsconfig.json b/packages/duplex-message/tsconfig.json index 649bfb4..843d263 100644 --- a/packages/duplex-message/tsconfig.json +++ b/packages/duplex-message/tsconfig.json @@ -1,39 +1,24 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist", - "module": "commonjs", - "target": "ES2017", - "lib": [ - "ES2017", - "DOM" - ], "types": [ + "node", "vite/client", - "@vitest/browser/providers/playwright" + "@vitest/browser-playwright" ], - "strict": true, - "allowJs": true, - "checkJs": true, - "skipLibCheck": true, - "declaration": true, - "noImplicitAny": true, - "esModuleInterop": true, - "resolveJsonModule": true, - "noImplicitOverride": true, "paths": { "src/*": [ - "src/*" + "./src/*" ] - }, + } }, "include": [ "src", "test", - "demo" + "*.mts" ], "exclude": [ "dist", - "vite.config.ts" + "demo" ] -} \ No newline at end of file +} diff --git a/packages/duplex-message/vite.config.mts b/packages/duplex-message/vite.config.mts new file mode 100644 index 0000000..416f227 --- /dev/null +++ b/packages/duplex-message/vite.config.mts @@ -0,0 +1,48 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' + +export default defineConfig(({ mode }) => ({ + define: mode === 'production-lib' ? { 'process.env.NODE_ENV': '"production"' } : {}, + resolve: { alias: { src: fileURLToPath(new URL('./src', import.meta.url)) } }, + server: { open: '/demo/index.html' }, + build: mode === 'demo' ? { + outDir: 'demo/dist', + rolldownOptions: { + input: { + index: 'demo/index.html', + ...Object.fromEntries(['worker', 'frame', 'page', 'storage', 'broadcast'] + .map((name) => [name, `demo/${name}/index.html`])), + childFrame: 'demo/frame/frame.html', + nestedFrame: 'demo/frame/sub-frame.html', + }, + }, + } : { + target: 'es2018', + emptyOutDir: mode !== 'production-lib', + lib: { + entry: 'src/index.ts', + name: 'duplex-message', + formats: mode === 'production-lib' ? ['es', 'umd'] : ['es', 'cjs', 'umd'], + fileName: (format) => mode === 'production-lib' + ? format === 'es' ? 'index.production.es.mjs' : 'index.production.umd.js' + : format === 'es' ? 'index.es.mjs' : format === 'cjs' ? 'index.cjs' : 'index.umd.js', + }, + rolldownOptions: { + output: { + // Retain the existing distribution URLs for browser/script consumers. + plugins: [{ + name: 'legacy-filenames', + generateBundle(_options, bundle) { + for (const chunk of Object.values(bundle)) { + if (chunk.type !== 'chunk') continue + const names = chunk.fileName === 'index.es.mjs' + ? ['index.es.js'] : chunk.fileName === 'index.production.es.mjs' + ? ['index.production.es.js'] : [] + for (const fileName of names) this.emitFile({ type: 'asset', fileName, source: chunk.code }) + } + }, + }], + }, + }, + }, +})) diff --git a/packages/duplex-message/vite.config.ts b/packages/duplex-message/vite.config.ts deleted file mode 100644 index 4552e7e..0000000 --- a/packages/duplex-message/vite.config.ts +++ /dev/null @@ -1,41 +0,0 @@ -/// -import { defineConfig } from 'vite' - -export default defineConfig(({ mode }) => { - if (mode === 'development') { - return { - build: { - target: 'es2015', - rollupOptions: { - input: { - worker: './demo/worker/index.html', - frame: './demo/frame/index.html', - page: './demo/page/index.html', - storage: './demo/storage/index.html', - broadcast: './demo/broadcast/index.html', - }, - }, - }, - server: { - open: '/demo/index.html' - }, - alias: { - src: '/src', - }, - } - } - return { - test: { - include: ['test/**/*.{tb,tn}.ts', 'test/**/*.{tb,tn}.tsx'], - exclude: ['demo/**'], - coverage: { - provider: 'istanbul', - exclude: ['demo/**', 'test/**', 'dist/**', '*.config.ts'], - }, - alias: { - src: '/src', - }, - globals: true, - }, - } -}) diff --git a/packages/duplex-message/vitest.config.mts b/packages/duplex-message/vitest.config.mts new file mode 100644 index 0000000..d297b82 --- /dev/null +++ b/packages/duplex-message/vitest.config.mts @@ -0,0 +1,36 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser-playwright' + +export default defineConfig({ + resolve: { alias: { src: fileURLToPath(new URL('./src', import.meta.url)) } }, + test: { + coverage: { provider: 'v8', include: ['src/**/*.ts'] }, + projects: [ + { + extends: true, + test: { + name: 'unit', + include: ['test/**/*.tn.ts'], + environment: 'node', + env: { NODE_ENV: 'production' }, + }, + }, + { + extends: true, + test: { + name: 'browser', + globalSetup: 'test/cross-origin-server.ts', + include: ['test/**/*.tb.ts'], + env: { NODE_ENV: 'production' }, + browser: { + enabled: true, + headless: true, + provider: playwright(), + instances: [{ browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }], + }, + }, + }, + ], + }, +}) diff --git a/packages/duplex-message/vitest.workspace.ts b/packages/duplex-message/vitest.workspace.ts deleted file mode 100644 index 06fb78b..0000000 --- a/packages/duplex-message/vitest.workspace.ts +++ /dev/null @@ -1,45 +0,0 @@ -// vitest.workspace.ts -import { defineWorkspace } from 'vitest/config' - -export default defineWorkspace([ - { - test: { - // an example of file based convention, - // you don't have to follow it - include: [ - 'test/**/*.tn.ts', - ], - name: 'unit', - alias: { - src: '/src', - }, - env: { - NODE_ENV: 'production', - }, - environment: 'node', - }, - }, - { - test: { - // an example of file based convention, - // you don't have to follow it - include: [ - 'test/**/*.tb.ts', - ], - alias: { - src: '/src', - test: '/test', - }, - env: { - NODE_ENV: 'development', - }, - name: 'browser', - browser: { - provider: 'playwright', - enabled: true, - name: 'chromium', - headless: true, - }, - }, - }, -]) diff --git a/packages/simple-electron-ipc/README.md b/packages/simple-electron-ipc/README.md index c078106..6651010 100644 --- a/packages/simple-electron-ipc/README.md +++ b/packages/simple-electron-ipc/README.md @@ -1,344 +1,220 @@ -

Simple-Electron-IPC

- - - -

an easy way to use electron ipc, get a response via promise, even with progress feedback support

- -## 📝 Table of Contents -- [Features](#features) -- [Install](#install) -- [Example](#example) -- [Usage](#usage) - - [MainMessageHub \& RendererMessageHub](#mainmessagehub--renderermessagehub) - - [emit](#emit) - - [on](#on) - - [progress](#progress) - - [off](#off) - - [destroy](#destroy) - - [Error](#error) - - [Debug](#debug) -## Features -* **Simple API**: `on` `emit` and `off` are all you need -* **Responsible**: `emit` will return a promise with the response from the other side -* **Progress-able**: get response with progress easily -* **Tinny**: less than 4kb gzipped(even smaller with tree-shaking), no external dependencies required -* **Consistency**: same api every where -* **Typescript support**: this utility is written in typescript, has type definition inborn - -It also has a browser version that can simplify cross window / js context messaging, check [Duplex-Message -](https://github.com/oe/duplex-message/tree/master/packages/duplex-message) for more details. +# simple-electron-ipc -## Install -using yarn -```sh -yarn add simple-electron-ipc -``` +**Bidirectional Electron RPC with progress feedback, typed calls and local cancellation.** Use it in the main process and an isolated preload; expose specific application operations through `contextBridge`. -or npm -```sh -npm install simple-electron-ipc -S -``` +[![CI](https://github.com/oe/duplex-message/actions/workflows/main.yml/badge.svg)](https://github.com/oe/duplex-message/actions) +[![npm](https://img.shields.io/npm/v/simple-electron-ipc.svg)](https://www.npmjs.com/package/simple-electron-ipc) -## Example +Use it for long-running tasks with progress, or when both main → preload and preload → main need request/response calls. For a few ordinary renderer → main requests, Electron's native [`ipcRenderer.invoke` / `ipcMain.handle`](https://www.electronjs.org/docs/latest/tutorial/ipc) may be enough. -The following example shows you how to use it to to communicate between electron app's main process and renderer process. +For browser Workers, iframes and tabs, see [duplex-message](https://github.com/oe/duplex-message/tree/main/packages/duplex-message#readme). -in main process: -```js -import { MainMessageHub } from 'simple-electron-ipc' - -const mainMessageHub = new MainMessageHub() - -// listen getUserToken and download from all renderer processes -mainMessageHub.on('*', { - getUserToken: (a, b) => Math.random().toString(36) + a + b, - - // download with progress support - download: (msg) => { - return new Promise((resolve, reject) => { - let hiCount = 0 - const tid = setInterval(() => { - if (hiCount >= 100) { - clearInterval(tid) - return resolve('done') - } - msg.onprogress({count: hiCount += 10}) - }, 200) - }) - } -} -// mainWindow should be an instance of BrowserWindow, use mainWindow.webContents to get WebContents object -mainMessageHub.on(mainWindow.webContents, 'some-method', () => {...}) +## Install -mainMessageHub.emit(mainWindow.webContents, 'generate-watermark', 'arg1', 'arg2') - .then(base64OfPng => {...}) - .catch(e => console.log(e)) +```sh +pnpm add simple-electron-ipc +# or: npm install simple-electron-ipc ``` -in renderer process: -```js -import { RendererMessageHub } from 'simple-electron-ipc' +Your application also needs Electron. The library's runtime dependency on `duplex-message` is installed automatically; shared RPC helpers and types are re-exported here. -const rendererMessageHub = new RendererMessageHub() +## Quick start with context isolation -rendererMessageHub.on('generate-watermark', async (arg1, arg2) => { - ... -}) +Keep `nodeIntegration: false`, `contextIsolation: true` and `sandbox: true`. Instantiate `RendererMessageHub` in **preload**, not the unprivileged page. Expose a fixed application API rather than the hub, raw IPC or arbitrary method names. -rendererMessageHub.emit('download', { - onprogress(p) { - console.log('progress', p) - } -}).then(res => console.log(res)) +**Shared application contract (`api.ts`):** +```ts +export interface MainApi { + add(a: number, b: number): number +} ``` -## Usage -### MainMessageHub & RendererMessageHub -Before use this lib to communicate to each other, you need to create instances with `MainMessageHub` for main process & `RendererMessageHub` for renderer process. - +**Main process:** ```ts -// in main process +import { app, BrowserWindow } from 'electron' import { MainMessageHub } from 'simple-electron-ipc' +import { join } from 'node:path' +import { pathToFileURL } from 'node:url' +import type { MainApi } from './api' + +void app.whenReady().then(() => { + const file = join(__dirname, 'index.html') + const trustedUrl = pathToFileURL(file).href + const window = new BrowserWindow({ + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + sandbox: true, + preload: join(__dirname, 'preload.cjs'), + }, + }) + const target = window.webContents + const hub = new MainMessageHub({ + validateSender: event => event.sender === target + && event.senderFrame === target.mainFrame + && event.senderFrame.url === trustedUrl, + }) + hub.on(target, { + add(a, b) { + if (!Number.isFinite(a) || !Number.isFinite(b)) throw new TypeError('numbers required') + return a + b + }, + } satisfies MainApi) + target.setWindowOpenHandler(() => ({ action: 'deny' })) + target.on('will-navigate', event => event.preventDefault()) + window.on('closed', () => hub.destroy()) + void window.loadFile(file) +}) +``` -const mainMessageHub = new MainMessageHub(options?: IElectronMessageHubOptions) - -// in renderer process -import { RendererMessageHub } from 'simple-electron-ipc' - -const rendererMessageHub = new RendererMessageHub(options?: IElectronMessageHubOptions) - +**Preload (`preload.ts`, bundled to `preload.cjs`):** -interface IElectronMessageHubOptions { - /** ipc channel name used under the hood, default: message-hub */ - channelName?: string -} +```ts +import { contextBridge } from 'electron' +import { RendererMessageHub, createRpcClient } from 'simple-electron-ipc' +import type { MainApi } from './api' + +const hub = new RendererMessageHub({ requestTimeout: 5000 }) +const main = createRpcClient((method, ...args) => hub.emit(method, ...args)) +contextBridge.exposeInMainWorld('appApi', { + add: (a: number, b: number) => main.call('add', a, b), +}) +window.addEventListener('unload', () => hub.destroy()) ``` -Tips: -> in most cases, you only need one instance in main process, you can use ->> `MainMessageHub.shared`/`RendererMessageHub.shared` instead of new an instance. -> e.g.: -> `MainMessageHub.shared.on(webContent, 'xxx', () => {...})` -> `RendererMessageHub.shared.emit('xxx').then((res) => {...})` - -If you change `channelName` when creating instances, main and renderers should use the same channel name. +**Renderer page:** -You may need to change `webPreferences` when create BrowserWindow, so that you can import `simple-electron-ipc` in renderer process: ```js -import { BrowserWindow } from "electron"; -const mainWindow = new BrowserWindow({ - webPreferences: { - nodeIntegration: true, - contextIsolation: false, - enableRemoteModule: true, - }, - // other configurations - ... - }); +const sum = await window.appApi.add(2, 3) // 5 ``` -The usage of `MainMessageHub` and `RendererMessageHub` have subtle differences, because the main process can send messages to multi renderers, but a renderer process can only send messages to the main process. +Register main handlers before loading the window. For application initialization that happens later, use the [explicit readiness endpoint](https://github.com/oe/duplex-message/tree/main/packages/duplex-message#wait-for-application-readiness); expose a named operation or ready promise through your bridge rather than raw `emit`. +### Bundle the sandboxed preload -### emit -Send a message to peer, invoking `methodName` registered on the peer via [`on`](#on) with all its arguments `args`: +A sandboxed preload has restricted `require` support. Bundle the library and its dependencies into the preload, leaving **only `electron` external**. Build a normal browser bundle for renderer code; it should not import Electron or this library at runtime. -```ts -// in main process -// if you got a BrowserWindow instance, use browserWindow.webContents to get WebContents -mainMessageHub.emit(peer: WebContents, method: string, ...args: any[]) => Promise +For example, a Vite preload build can use: -// in renderer process, no need to specify the peer, the peer is default to the main process -rendererMessageHub.emit(method: string, ...args: any[]) => Promise +```ts +import { defineConfig } from 'vite' + +export default defineConfig({ + define: { 'process.env.NODE_ENV': JSON.stringify('production') }, + build: { + outDir: 'dist', + lib: { entry: 'src/preload.ts', formats: ['cjs'], fileName: () => 'preload.cjs' }, + rolldownOptions: { external: ['electron'] }, + }, +}) ``` -e.g. -```js -// in main process -const mainWindow = new BrowserWindow({....}) -mainMessageHub - .emit(mainWindow.webContents, 'some-method', 'arg1', 'arg2') - .then(res => console.log('success', res)) - .catch(err => console.warn('error', err)) - -// in renderer process -rendererMessageHub - .emit('stop-download') - .then(res => console.log('success', res)) - .catch(err => console.warn('error', err)) +The [runnable demo](https://github.com/oe/duplex-message/tree/main/packages/simple-electron-ipc/demo) includes main, bundled preload, renderer, shared interfaces and a Content Security Policy. From the repository root: + +```sh +pnpm install +pnpm build +pnpm --filter simple-electron-ipc dev ``` -Notice: -1. look into [Error](#error) when you catch an error -2. omit args if no arguments are required, e.g `rendererMessageHub.emit('some-method')` -3. you may need to handle the promise returned by `emit` if some lint warning unhandled promise(or floating promise) +## Typed calls and both directions -### on -Listen messages sent from peer, it has following forms: +`createRpcClient` checks method names and parameters and infers awaited results. Types describe the expected API; handlers must still validate actual application data. ```ts -// in main process -// register(listen)) one handler for methodName when message received from renderer -// * means all renderers, same as below -mainMessageHub.on(peer: WebContents | '*', methodName: string, handler: Function) -// register(listen)) multi handlers -mainMessageHub.on(peer: WebContents | '*', handlerMap: Record) -// register only one handler to deal with all messages from renderer -mainMessageHub.on(peer: WebContents | '*', singleHandler: Function) - - -// in renderer process -// register(listen)) one handler for methodName when message received from main process -rendererMessageHub.on(methodName: string, handler: Function) -// register(listen)) multi handlers -rendererMessageHub.on(handlerMap: Record) -// register only one handler to deal with all messages from process -rendererMessageHub.on(singleHandler: Function) +// In main, addressing a particular WebContents: +const renderer = createRpcClient((method, ...args) => mainHub.emit(webContents, method, ...args)) +const title = await renderer.call('pageTitle') + +// In preload, addressing main: +const main = createRpcClient((method, ...args) => rendererHub.emit(method, ...args)) +const sum = await main.call('add', 2, 3) +// main.call('add', '2', 3) // TypeScript error. ``` -e.g. -```js -// in main process -const mainWindow = new BrowserWindow({....}) -// listen multi messages from mainWindow by passing a handler map -mainMessageHub.on(mainWindow.webContents, { - hi (name) { - console.log(`name ${name}`) - // response by return - return `hi ${name}` - }, - 'some-method': function (a, b) { - ... - } -}) -// listen 'get-token' from all renderers -mainMessageHub.on('*', 'get-token', () => Math.random().toString(36).slice(2) ) +Register `pageTitle` in the preload with `rendererHub.on('pageTitle', () => document.title)`. The original generic `emit` API is also available. +## Progress and local cancellation -// in renderer process -rendererMessageHub.on('async-add', async function (a, b) { - return new Promise((resolve, reject) => { - resolve(a + b) - }) -}) +Supply `onprogress` in the first argument. The main handler receives a corresponding callback: -rendererMessageHub.on({ - 'method1': function () {...}, - 'method2': function (a, b, c) {...} +```ts +mainHub.on(webContents, 'export', async (options: { onprogress: (percent: number) => void }) => { + for (let percent = 10; percent <= 100; percent += 10) { + await new Promise(resolve => setTimeout(resolve, 50)) + options.onprogress(percent) + } + return 'done' }) +``` + +In preload, keep the signal local and expose named start/cancel operations: -// listen all messages from main process with one handler -anotherWindowRendererMessageHub.on((methodName, ...args) => { - ... +```ts +let pending: AbortController | undefined +contextBridge.exposeInMainWorld('exportApi', { + async start(onprogress: (percent: number) => void) { + if (typeof onprogress !== 'function') throw new TypeError('callback required') + if (pending) throw new Error('an export is already pending') + const controller = new AbortController() + pending = controller + try { + return await rendererHub.emit({ + methodName: 'export', requestTimeout: 10_000, signal: controller.signal, + }, { onprogress }) + } finally { pending = undefined } + }, + cancel: () => pending?.abort(), }) ``` -Notice: -1. you should only listen a message once, it will override existing listener when do it again -2. for `mainMessageHub`: the specified callback will be called if you listen same `methodName` in specified peer and `*` +The renderer calls `window.exportApi.start(percent => updateProgress(percent))` and handles its promise rejection; a Cancel button calls `window.exportApi.cancel()`. -### progress -If you need progress feedback when peer handling you requests, you can do it by setting the first argument as an object and has a function property named `onprogress` when `emit` messages, and call `onprogress` in `on` on the peer's side. +Cancellation stops **local waiting**, not an already running main handler. The signal and deadline are not sent through IPC or `contextBridge`. Remote work can continue after cancellation. Timers and abort listeners are cleared, and late progress/results are ignored. -e.g. -```js -// in main process -// listen download from all renderer processes -mainMessageHub.on('*', { - // download with progress support - download: (msg) => { - return new Promise((resolve, reject) => { - let hiCount = 0 - const tid = setInterval(() => { - if (hiCount >= 100) { - clearInterval(tid) - return resolve('done') - } - // send feedback by calling onprogress if it exists - msg && msg.onprogress && msg.onprogress({count: hiCount += 10}) - }, 200) - }) - } -} +A total `requestTimeout` includes heartbeat wait and is not extended by progress. The default is `0` (unlimited). Set a constructor default or override it in method configuration; `0` disables that default. Values must be finite numbers from `0` to `2147483647` milliseconds. -// in renderer process -rendererMessageHub.emit('download', { - onprogress(p) {console.log('progress: ' + p.count)} -}).then(e => { - console.log('success: ', e) -}).catch(err => { - console.log('error: ' + err) -}) -``` +## Sender validation and application boundaries -### off -Remove message handlers, if `methodName` presented, remove `methodName`'s listener, or remove the whole peer's listener +`MainMessageHub` accepts a synchronous `validateSender(event: IpcMainEvent): boolean`. It runs before any incoming request, progress or response is processed. Only returning `true` accepts the message; any other value or a thrown error ignores it. Async validators are unsupported. Validate both the expected `WebContents` and trusted frame/URL; a registered `WebContents` can contain untrusted child frames. -```ts -// in main process -mainMessageHub.off(peer: WebContents | '*', methodName?: string) +The hook is optional for compatibility. Configure it for privileged operations, use a narrow bridge API, and validate method arguments and permissions. See [Electron's security guidance](https://www.electronjs.org/docs/latest/tutorial/security#17-validate-the-sender-of-all-ipc-messages). +## API reference -// in renderer process -rendererMessageHub.off(methodName?: string) -``` +| Operation | Main | Preload | +| --- | --- | --- | +| Register a method | `main.on(webContents, name, handler)` | `renderer.on(name, handler)` | +| Register a map | `main.on(webContents, handlers)` | `renderer.on(handlers)` | +| Call a method | `main.emit(webContents, method, ...args)` | `renderer.emit(method, ...args)` | +| Remove a method | `main.off(webContents, name)` | `renderer.off(name)` | +| Remove all peer methods | `main.off(webContents)` | `renderer.off()` | +| Release a hub | `main.destroy()` | `renderer.destroy()` | -### destroy -Destroy instance: remove all message handlers and references of objects. -Any invoking of destroyed instance's methods will throw an exception +`method` is a string or `IMethodNameConfig` with `{ methodName, to?, requestTimeout?, signal? }`. Main can use `'*'` for fallback registration; use peer-specific registration and sender validation for application operations. Both classes also support catch-all handlers and a `shared` getter. Destroy is idempotent, rejects pending calls with `UNKNOWN`, and releases listeners. -```ts -// in main process -mainMessageHub.destroy() +Both constructors support `channelName` (default `message-hub`), `instanceID`, `heartbeatTimeout` (default 500ms) and `requestTimeout` (default `0`). Main additionally supports `validateSender`. Use the same channel name on both sides. +Errors inside the hub conform to `IError`: `{ code, message, error? }`. `EErrorCode` and shared helper types can be imported from `simple-electron-ipc`: -// in renderer process -rendererMessageHub.destroy() -``` +| Code | Name | +| --- | --- | +| 1 | `HANDLER_EXEC_ERROR` | +| 2 | `PEER_NOT_FOUND` | +| 3 | `METHOD_NOT_FOUND` | +| 4 | `INVALID_MESSAGE` | +| 5 | `UNKNOWN` | +| 6 | `REQUEST_ABORTED` | +| 7 | `REQUEST_TIMEOUT` | -### Error -when you catch an error from `emit`, it conforms the following structure `IError` +Error objects crossing `contextBridge` do not necessarily retain custom properties. If your page needs structured error codes, catch errors in preload and return a narrow application result such as `{ ok: false, code, message }`. -```ts -/** error object could be caught via emit().catch(err) */ -interface IError { - /** none-zero error code */ - code: EErrorCode - /** error message */ - message: string - /** error object if it could pass through via the message channel underground*/ - error?: Error -} +ESM/CommonJS imports and type declarations are available. Shared RPC declarations require TypeScript 4.1 or later; the installed Electron version may require a newer compiler for its own declarations. Consumers do not need the workspace's TypeScript 7 compiler. Library output targets ES2018, with packed JavaScript syntax checked before release. When upgrading from 2.1, handle pending-call rejections on `destroy()` and upgrade compilers older than 4.1. `signal` and `requestTimeout` in method configuration are reserved local controls. CI exercises mocked IPC and a real main/preload/renderer application with Electron 31 and 44 on Linux, and Electron 44 on Windows/macOS. Other Electron versions still require application-level verification. -/** enum of error code */ -enum EErrorCode { - /** handler on other side encounter an error */ - HANDLER_EXEC_ERROR = 1, - /** peer not found */ - PEER_NOT_FOUND = 2, - /** method not found in peer */ - METHOD_NOT_FOUND = 3, - /** message has invalid content, can't be sent */ - INVALID_MESSAGE = 4, - /** other unspecified error */ - UNKNOWN = 5, -} -``` +## Development and license + +See [workspace development instructions](https://github.com/oe/duplex-message#development). `pnpm check:electron` builds and runs the real Electron smoke test; Linux runners need a display, for example `xvfb-run -a pnpm check:electron`. Leave `ELECTRON_SKIP_BINARY_DOWNLOAD` unset when installing to run the demo or smoke test. -### Debug -You can enable debug mode by setting `process.env.NODE_ENV` to any value other than `production`, like `development`, it will log some debug info to the console. +MIT. See [LICENSE](https://github.com/oe/duplex-message/blob/main/LICENSE). diff --git a/packages/simple-electron-ipc/demo/api.ts b/packages/simple-electron-ipc/demo/api.ts new file mode 100644 index 0000000..f0d4707 --- /dev/null +++ b/packages/simple-electron-ipc/demo/api.ts @@ -0,0 +1,21 @@ +export interface MainMethods { + download(options: { onprogress: (count: number) => void }): Promise + getTitle(prefix: string): Promise + calculate(a: number, b: number): Promise +} + +export interface PreloadMethods { + pageTitle(): string + addNumbers(a: number, b: number): number +} + +export interface DemoApi { + download(onprogress: (count: number) => void): Promise + cancelDownload(): void + getTitle(prefix: string): Promise + calculate(a: number, b: number): Promise +} + +declare global { + interface Window { duplexDemo: DemoApi } +} diff --git a/packages/simple-electron-ipc/demo/index.html b/packages/simple-electron-ipc/demo/index.html index 06c3fdd..773efd8 100644 --- a/packages/simple-electron-ipc/demo/index.html +++ b/packages/simple-electron-ipc/demo/index.html @@ -1,56 +1,20 @@ - - - + + - - Hello World! - + + + Duplex Message Electron Demo -

Hello World!

- We are using Node.js , Chromium - , and Electron - . - -
- -
- response -
-
- - -
- response -
-
- - -
- response -
-
- - -
- response -
-
- - -
- response -
-
-
- - +

Electron RPC with an isolated preload

+

Progress, local cancellation and typed calls through a fixed contextBridge API.

+ + +

+ +

+ +

- - - \ No newline at end of file + diff --git a/packages/simple-electron-ipc/demo/main.ts b/packages/simple-electron-ipc/demo/main.ts index 0d559d2..81740c4 100644 --- a/packages/simple-electron-ipc/demo/main.ts +++ b/packages/simple-electron-ipc/demo/main.ts @@ -1,86 +1,73 @@ -import { app, BrowserWindow } from "electron"; -import { MainMessageHub } from 'simple-electron-ipc' -import * as path from "path"; +import { app, BrowserWindow, type WebContents } from 'electron' +import { MainMessageHub, createRpcClient } from 'simple-electron-ipc' +import { join } from 'node:path' +import { pathToFileURL } from 'node:url' +import type { MainMethods, PreloadMethods } from './api' - -let mainWindow //: BrowserWindow -const messageHub = new MainMessageHub() -messageHub.on('*', { - getUserToken: (a, b) => Math.random().toString(36) + a + b, - download: (msg) => { - return new Promise((resolve, reject) => { - let hiCount = 0 - const tid = setInterval(() => { - if (hiCount >= 100) { - clearInterval(tid) - return resolve('done') - } - msg.onprogress({count: hiCount += 10}) - }, 200) - }) +const trusted = new Map() +const hub = new MainMessageHub({ + requestTimeout: 5000, + validateSender(event) { + const url = trusted.get(event.sender) + return url !== undefined && event.senderFrame === event.sender.mainFrame + && event.senderFrame.url === url }, - // async getTitle (t: string) { - // console.log('getTitle', mainWindow.send) - // const title = await messageHub.emit(mainWindow, 'pageTitle') - // return t + '---' + title - // }, - calc (a, b) { - return messageHub.emit(mainWindow, 'addNumber', { - a, - b, - onprogress(e) {console.log('progress', e)} - }) - } }) - function createWindow() { - // Create the browser window. - mainWindow = new BrowserWindow({ - height: 600, + const window = new BrowserWindow({ + width: 800, height: 600, webPreferences: { - nodeIntegration: true, - contextIsolation: false, - preload: path.join(__dirname, "preload.js"), + nodeIntegration: false, + contextIsolation: true, + sandbox: true, + preload: join(__dirname, 'preload.cjs'), }, - width: 800, - }); - // mainWindow.send() - - // and load the index.html of the app. - mainWindow.loadFile(path.join(__dirname, "../index.html")); - - // Open the DevTools. - mainWindow.webContents.openDevTools(); - - messageHub.on(mainWindow.webContents, 'getTitle', async function (t: string) { - console.log('getTitle', mainWindow.send) - const title = await messageHub.emit(mainWindow.webContents, 'pageTitle') - return t + '---' + title }) + const file = join(__dirname, '../index.html') + const target = window.webContents + trusted.set(target, pathToFileURL(file).href) + target.setWindowOpenHandler(() => ({ action: 'deny' })) + target.on('will-navigate', event => event.preventDefault()) + const timers = new Set>() + const renderer = createRpcClient((method, ...args) => hub.emit(target, method, ...args)) + hub.on(target, { + download(options) { + if (typeof options?.onprogress !== 'function') throw new TypeError('progress callback required') + return new Promise(resolve => { + let count = 0 + const timer = setInterval(() => { + count += 10 + options.onprogress(count) + if (count === 100) { + clearInterval(timer) + timers.delete(timer) + resolve('done') + } + }, 50) + timers.add(timer) + }) + }, + async getTitle(prefix) { + if (typeof prefix !== 'string') throw new TypeError('prefix must be a string') + return prefix + await renderer.call('pageTitle') + }, + calculate(a, b) { + if (!Number.isFinite(a) || !Number.isFinite(b)) throw new TypeError('numbers required') + return renderer.call('addNumbers', a, b) + }, + } satisfies MainMethods) + window.on('closed', () => { + timers.forEach(clearInterval) + trusted.delete(target) + hub.off(target) + }) + void window.loadFile(file) } -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.whenReady().then(() => { - createWindow(); - - app.on("activate", function () { - // On macOS it's common to re-create a window in the app when the - // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) createWindow(); - }); -}); - -// Quit when all windows are closed, except on macOS. There, it's common -// for applications and their menu bar to stay active until the user quits -// explicitly with Cmd + Q. -app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - app.quit(); - } -}); - -// In this file you can include the rest of your app"s specific main process -// code. You can also put them in separate files and require them here. +void app.whenReady().then(() => { + createWindow() + app.on('activate', () => { if (!BrowserWindow.getAllWindows().length) createWindow() }) +}) +app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) +app.on('will-quit', () => hub.destroy()) diff --git a/packages/simple-electron-ipc/demo/preload.ts b/packages/simple-electron-ipc/demo/preload.ts index 70a0c1b..23f2cd4 100644 --- a/packages/simple-electron-ipc/demo/preload.ts +++ b/packages/simple-electron-ipc/demo/preload.ts @@ -1,17 +1,32 @@ -import { ipcRenderer } from 'electron'; -// All of the Node.js APIs are available in the preload process. -// It has the same sandbox as a Chrome extension. -window.addEventListener("DOMContentLoaded", () => { - const replaceText = (selector: string, text: string) => { - const element = document.getElementById(selector); - if (element) { - element.innerText = text; - } - }; +import { contextBridge } from 'electron' +import { RendererMessageHub, createRpcClient } from 'simple-electron-ipc' +import type { DemoApi, MainMethods, PreloadMethods } from './api' - for (const type of ["chrome", "node", "electron"]) { - // @ts-ignore - replaceText(`${type}-version`, process.versions[type as keyof NodeJS.ProcessVersions]); - } -}); -console.log(ipcRenderer) \ No newline at end of file +const hub = new RendererMessageHub({ requestTimeout: 5000 }) +const main = createRpcClient((method, ...args) => hub.emit(method, ...args)) +hub.on({ + pageTitle: () => document.title, + addNumbers: (a, b) => { + if (!Number.isFinite(a) || !Number.isFinite(b)) throw new TypeError('numbers required') + return a + b + }, +} satisfies PreloadMethods) + +let downloadController: AbortController | undefined +const api: DemoApi = { + async download(onprogress) { + if (typeof onprogress !== 'function') throw new TypeError('callback required') + if (downloadController) throw new Error('a download is already pending') + const controller = new AbortController() + downloadController = controller + try { + return await main.call({ methodName: 'download', signal: controller.signal }, { onprogress }) + } finally { downloadController = undefined } + }, + cancelDownload: () => downloadController?.abort(), + getTitle: prefix => main.call('getTitle', prefix), + calculate: (a, b) => main.call('calculate', a, b), +} +// Expose specific application operations; keep hubs, channel names and signals in preload. +contextBridge.exposeInMainWorld('duplexDemo', api) +window.addEventListener('unload', () => { downloadController?.abort(); hub.destroy() }) diff --git a/packages/simple-electron-ipc/demo/render.ts b/packages/simple-electron-ipc/demo/render.ts index 44e5ce8..1378f13 100644 --- a/packages/simple-electron-ipc/demo/render.ts +++ b/packages/simple-electron-ipc/demo/render.ts @@ -1,66 +1,17 @@ -// @ts-ignore -export = 0 -import { RendererMessageHub } from 'simple-electron-ipc' +import type { DemoApi } from './api' +const api: DemoApi = window.duplexDemo +const text = (id: string, value: string) => { document.getElementById(id)!.textContent = value } +const message = (error: unknown) => error instanceof Error ? error.message : String(error) -const messageHub = new RendererMessageHub() -// @ts-ignore -window.messageHub = messageHub -messageHub.on({ - pageTitle () { - return document.title - }, - addNumber (msg: any) { - return new Promise((resolve, reject) => { - let hiCount = 0 - const tid = setInterval(() => { - if (hiCount >= 100) { - clearInterval(tid) - return resolve(msg.a + msg.b) - } - msg.onprogress({count: hiCount += 10}) - }, 200) - }) - }, +document.getElementById('download')!.addEventListener('click', () => { + void api.download(count => text('download-resp', `Progress: ${count}%`)) + .then(result => text('download-resp', result), error => text('download-resp', message(error))) }) - -const $ = (id: string) => { - return document.getElementById(id.replace(/^\#/, '')) as HTMLElement -} - - -$('download').addEventListener('click', () => { - messageHub.emit('download', { - onprogress(e) {$('download-resp').innerHTML = 'progress: ' + JSON.stringify(e)} - }).then(e => { - $('download-resp').innerHTML = 'success: ' + JSON.stringify(e) - }) +document.getElementById('cancel')!.addEventListener('click', () => api.cancelDownload()) +document.getElementById('get-title')!.addEventListener('click', () => { + void api.getTitle('Title: ').then(result => text('get-title-resp', result), error => text('get-title-resp', message(error))) }) - -$('get-token').addEventListener('click', () => { - messageHub.emit('getUserToken', 'xiu', 'saiya').then((e) => { - $('get-token-resp').innerHTML = 'success: ' + JSON.stringify(e) - }) -}) - -$('error').addEventListener('click', () => { - messageHub.emit('aaa', 'xiu', 'saiya').then((e) => { - $('error-resp').innerHTML = 'success: ' + JSON.stringify(e) - }) - .catch((e) => { - $('error-resp').innerHTML = 'error: ' + JSON.stringify(e) - }) -}) - - -$('get-title').addEventListener('click', () => { - messageHub.emit('getTitle', 'my-xiu').then((e) => { - $('get-title-resp').innerHTML = 'success: ' + JSON.stringify(e) - }) -}) - -$('get-calc').addEventListener('click', () => { - messageHub.emit('calc', 99, 2323).then((e) => { - $('get-calc-resp').innerHTML = 'success: ' + JSON.stringify(e) - }) +document.getElementById('get-calc')!.addEventListener('click', () => { + void api.calculate(2, 3).then(result => text('get-calc-resp', String(result)), error => text('get-calc-resp', message(error))) }) diff --git a/packages/simple-electron-ipc/demo/tsconfig.json b/packages/simple-electron-ipc/demo/tsconfig.json new file mode 100644 index 0000000..3cdaf5f --- /dev/null +++ b/packages/simple-electron-ipc/demo/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "dist", + "noImplicitAny": true + }, + "include": [ + "*.ts", + "*.mts" + ] +} diff --git a/packages/simple-electron-ipc/demo/vite.config.mts b/packages/simple-electron-ipc/demo/vite.config.mts new file mode 100644 index 0000000..124ac93 --- /dev/null +++ b/packages/simple-electron-ipc/demo/vite.config.mts @@ -0,0 +1,23 @@ +import { defineConfig } from 'vite' +import { fileURLToPath } from 'node:url' +import { join } from 'node:path' + +const root = fileURLToPath(new URL('.', import.meta.url)) +export default defineConfig(({ mode }) => { + const preload = mode === 'preload' + return { + define: { 'process.env.NODE_ENV': JSON.stringify('production') }, + build: { + target: 'es2020', + outDir: join(root, 'dist'), + emptyOutDir: false, + lib: { + entry: join(root, preload ? 'preload.ts' : 'render.ts'), + name: 'DuplexDemo', + formats: preload ? ['cjs'] : ['iife'], + fileName: () => preload ? 'preload.cjs' : 'render.js', + }, + rolldownOptions: { external: preload ? ['electron'] : [] }, + }, + } +}) diff --git a/packages/simple-electron-ipc/package.json b/packages/simple-electron-ipc/package.json index f3b0f90..0f20d90 100644 --- a/packages/simple-electron-ipc/package.json +++ b/packages/simple-electron-ipc/package.json @@ -1,43 +1,71 @@ { "name": "simple-electron-ipc", "version": "2.1.0", - "description": "an easy way to use electron ipc, get a response via promise, event support progress feedback", + "description": "Bidirectional Electron RPC with progress feedback, typed calls and local cancellation; use from an isolated preload.", "main": "dist/index.js", - "typings": "dist/index.d.ts", - "module": "dist/index.es.js", + "module": "dist/index.es.mjs", "scripts": { - "build": "vite build", - "prepublish": "pnpm build", - "predev": "tsc demo/*.ts --outDir demo/dist -t es5", + "build": "vite build && tsc -p tsconfig.build.json", + "typecheck": "tsc --noEmit", + "prepublishOnly": "pnpm build", + "test": "vitest run", + "build:demo": "tsc -p demo/tsconfig.json && vite build --config demo/vite.config.mts --mode preload && vite build --config demo/vite.config.mts --mode renderer", + "predev": "pnpm build && pnpm build:demo", "dev": "electron ./demo/dist/main.js" }, "files": [ "dist/*" ], "keywords": [ - "rpc", - "duplex", - "ipc", - "duplex-message", - "hub", "electron", - "promise" + "ipc", + "rpc", + "contextbridge", + "preload", + "context-isolation", + "typescript", + "promise", + "progress", + "bidirectional", + "ipc-main", + "ipc-renderer" ], "dependencies": { - "duplex-message": "workspace:" + "duplex-message": "workspace:^" }, "devDependencies": { - "electron": "catalog:", "typescript": "catalog:", "vite": "catalog:", - "vite-plugin-dts": "catalog:", - "vitest": "catalog:" + "vitest": "catalog:", + "electron": "catalog:" }, "peerDependencies": { "electron": "*" }, - "repository": "https://oe@github.com/oe/duplex-message.git", + "repository": { + "type": "git", + "url": "https://github.com/oe/duplex-message.git", + "directory": "packages/simple-electron-ipc" + }, "author": "Saiya", "license": "MIT", - "homepage": "https://github.com/oe/duplex-message/tree/master/packages/simple-electron-ipc" + "homepage": "https://github.com/oe/duplex-message/tree/main/packages/simple-electron-ipc", + "type": "commonjs", + "sideEffects": false, + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "node": { + "import": "./dist/index.node.mjs", + "require": "./dist/index.js" + }, + "import": "./dist/index.es.mjs", + "require": "./dist/index.js" + }, + "./dist/*": "./dist/*", + "./package.json": "./package.json", + "./dist/index": "./dist/index.js", + "./dist/index.es": "./dist/index.es.js" + } } diff --git a/packages/simple-electron-ipc/src/abstract.ts b/packages/simple-electron-ipc/src/abstract.ts index adb3e23..caf1441 100644 --- a/packages/simple-electron-ipc/src/abstract.ts +++ b/packages/simple-electron-ipc/src/abstract.ts @@ -41,8 +41,8 @@ export abstract class ElectronMessageHub extends AbstractHub { target.send(this._channelName, msg) } - protected override async onMessage(evt: IpcMainEvent | IpcRendererEvent, msg: any) { - super.onMessage(evt.sender, msg) + protected override onMessage(evt: IpcMainEvent | IpcRendererEvent, msg: any) { + return super.onMessage(evt.sender, msg) } override destroy(): void { diff --git a/packages/simple-electron-ipc/src/index.ts b/packages/simple-electron-ipc/src/index.ts index c41867c..96950ae 100644 --- a/packages/simple-electron-ipc/src/index.ts +++ b/packages/simple-electron-ipc/src/index.ts @@ -1,3 +1,5 @@ export * from './main' export * from './renderer' -export { setConfig } from 'duplex-message' +export { setConfig, createRpcClient, waitForPeer, READY_METHOD, EErrorCode } from 'duplex-message' +export type { IRpcClient, RpcEmitter, IWaitForPeerOptions, IMethodNameConfig, IError } from 'duplex-message' +export type { IElectronMessageHubOptions } from './abstract' diff --git a/packages/simple-electron-ipc/src/main.ts b/packages/simple-electron-ipc/src/main.ts index efa0118..0147e01 100644 --- a/packages/simple-electron-ipc/src/main.ts +++ b/packages/simple-electron-ipc/src/main.ts @@ -1,14 +1,39 @@ -import { WebContents } from 'electron' -import { IHandlerMap, IFn } from 'duplex-message' +import { WebContents, IpcMainEvent } from 'electron' +import { IHandlerMap, IFn, IMethodNameConfig } from 'duplex-message' import { ElectronMessageHub, IElectronMessageHubOptions } from './abstract' let sharedMainMessageHub: MainMessageHub +export interface IMainMessageHubOptions extends IElectronMessageHubOptions { + /** Validate the sender and frame before processing any incoming IPC message. */ + validateSender?: (event: IpcMainEvent) => boolean +} + export class MainMessageHub extends ElectronMessageHub { - constructor(options?: IElectronMessageHubOptions) { + private readonly _validateSender?: (event: IpcMainEvent) => boolean + + constructor(options?: IMainMessageHubOptions) { + if (options?.validateSender !== undefined && typeof options.validateSender !== 'function') { + throw new TypeError('validateSender must be a function') + } super({ ...options, type: 'browser' }, 'MainMessageHub') + this._validateSender = options?.validateSender + } + + protected override onMessage(event: IpcMainEvent, message: unknown) { + try { + if (this._validateSender) { + const allowed = this._validateSender(event) + if (allowed !== true) { + // An accidentally async validator must neither accept IPC nor crash main on rejection. + void Promise.resolve(allowed).catch(() => {}) + return Promise.resolve() + } + } + } catch { return Promise.resolve() } + return super.onMessage(event, message) } - emit(target: WebContents, method: string, ...args: any[]) { + emit(target: WebContents, method: string | IMethodNameConfig, ...args: any[]) { return super._emit(target, method, ...args) } @@ -29,7 +54,7 @@ export class MainMessageHub extends ElectronMessageHub { /** shared MainMessageHub instance */ public static get shared() { - if (!sharedMainMessageHub) { + if (!sharedMainMessageHub || sharedMainMessageHub.isDestroyed) { sharedMainMessageHub = new MainMessageHub() } return sharedMainMessageHub diff --git a/packages/simple-electron-ipc/src/renderer.ts b/packages/simple-electron-ipc/src/renderer.ts index 2f10544..b16df15 100644 --- a/packages/simple-electron-ipc/src/renderer.ts +++ b/packages/simple-electron-ipc/src/renderer.ts @@ -1,4 +1,4 @@ -import { IHandlerMap, IFn } from 'duplex-message' +import { IHandlerMap, IFn, IMethodNameConfig } from 'duplex-message' import { ElectronMessageHub, IElectronMessageHubOptions } from './abstract' let rendererMessageHub: RendererMessageHub @@ -7,7 +7,7 @@ export class RendererMessageHub extends ElectronMessageHub { super({ ...options, type: 'renderer' }, 'RendererMessageHub') } - emit(method: string, ...args: any[]) { + emit(method: string | IMethodNameConfig, ...args: any[]) { return super._emit(this._ipc, method, ...args) } @@ -24,7 +24,7 @@ export class RendererMessageHub extends ElectronMessageHub { /** shared MainMessageHub instance */ public static get shared() { - if (!rendererMessageHub) { + if (!rendererMessageHub || rendererMessageHub.isDestroyed) { rendererMessageHub = new RendererMessageHub() } return rendererMessageHub diff --git a/packages/simple-electron-ipc/test/ipc.test.ts b/packages/simple-electron-ipc/test/ipc.test.ts new file mode 100644 index 0000000..68323e1 --- /dev/null +++ b/packages/simple-electron-ipc/test/ipc.test.ts @@ -0,0 +1,208 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import type { WebContents } from 'electron' +import { MainMessageHub, RendererMessageHub, type IMainMessageHubOptions } from '../src/index' + +const transport = vi.hoisted(() => { + type Listener = (event: { sender: unknown; senderFrame: { url: string } }, data: unknown) => unknown + class Ipc { + listeners = new Map>() + on(channel: string, listener: Listener) { + const listeners = this.listeners.get(channel) ?? new Set() + listeners.add(listener) + this.listeners.set(channel, listeners) + } + off(channel: string, listener: Listener) { this.listeners.get(channel)?.delete(listener) } + receive(channel: string, sender: unknown, data: unknown) { + for (const listener of this.listeners.get(channel) ?? []) void listener({ sender, senderFrame: frame }, data) + } + } + const ipcMain = new Ipc() + const frame = { url: 'file:///app/index.html' } + const ipcRenderer = Object.assign(new Ipc(), { + send(channel: string, data: unknown) { + const message = structuredClone(data) + queueMicrotask(() => ipcMain.receive(channel, webContents, message)) + }, + }) + const webContents = { + send(channel: string, data: unknown) { + const message = structuredClone(data) + queueMicrotask(() => ipcRenderer.receive(channel, ipcRenderer, message)) + }, + } + return { ipcMain, ipcRenderer, webContents, frame } +}) + +vi.mock('electron', () => ({ default: transport })) + +const hubs: (MainMessageHub | RendererMessageHub)[] = [] +function inProcess(type: 'browser' | 'renderer') { + vi.stubGlobal('process', { ...process, type }) +} +function pair(channelName?: string, validateSender?: IMainMessageHubOptions['validateSender']) { + inProcess('browser') + const main = new MainMessageHub({ channelName, validateSender }) + inProcess('renderer') + const renderer = new RendererMessageHub({ channelName }) + hubs.push(main, renderer) + return { main, renderer, target: transport.webContents as unknown as WebContents } +} + +beforeEach(() => vi.useFakeTimers()) +afterEach(() => { + hubs.splice(0).forEach((hub) => hub.destroy()) + transport.ipcMain.listeners.clear() + transport.ipcRenderer.listeners.clear() + transport.frame.url = 'file:///app/index.html' + vi.unstubAllGlobals() + vi.useRealTimers() +}) + +describe('Electron IPC', () => { + it.each([undefined, 'true', 1, {}, Promise.resolve(false), Promise.resolve(true)])( + 'requires an explicit synchronous true from the sender validator (%j)', async value => { + const { main, renderer, target } = pair(undefined, (() => value) as unknown as IMainMessageHubOptions['validateSender']) + const handler = vi.fn(() => 'secret') + main.on(target, 'secret', handler) + const response = expect(renderer.emit('secret')).rejects.toMatchObject({ code: 3 }) + await vi.advanceTimersByTimeAsync(500) + await response + expect(handler).not.toHaveBeenCalled() + expect(vi.getTimerCount()).toBe(0) + }, + ) + + it('accepts a trusted sender and frame', async () => { + const { main, renderer, target } = pair(undefined, event => event.sender === transport.webContents + && event.senderFrame?.url === 'file:///app/index.html') + main.on(target, 'echo', () => 'trusted') + await expect(renderer.emit('echo')).resolves.toBe('trusted') + }) + + it('ignores an accidentally async throwing validator without an unhandled rejection', async () => { + const validate = async () => { throw new Error('cannot validate sender') } + const { main, renderer, target } = pair(undefined, validate as unknown as IMainMessageHubOptions['validateSender']) + const handler = vi.fn(() => 'secret') + main.on(target, 'secret', handler) + const response = expect(renderer.emit('secret')).rejects.toMatchObject({ code: 3 }) + await vi.advanceTimersByTimeAsync(500) + await response + expect(handler).not.toHaveBeenCalled() + }) + + it.each(['untrusted frame', 'throwing validator'])('ignores IPC from %s before running handlers', async scenario => { + const { main, renderer, target } = pair(undefined, event => { + if (scenario === 'throwing validator') throw new Error('cannot validate sender') + return event.senderFrame?.url === 'file:///app/index.html' + }) + transport.frame.url = 'https://untrusted.example' + const handler = vi.fn(() => 'secret') + main.on(target, 'secret', handler) + const response = expect(renderer.emit('secret')).rejects.toMatchObject({ code: 3 }) + await vi.advanceTimersByTimeAsync(500) + await response + expect(handler).not.toHaveBeenCalled() + expect(vi.getTimerCount()).toBe(0) + }) + + it('delivers reserved-string progress with request controls across serialized IPC', async () => { + const { main, renderer } = pair() + main.on('*', 'progress', (options: { onprogress: (value: string) => void }) => { + options.onprogress('--message-hub-to-be-continued--') + return 'done' + }) + const controller = new AbortController() + const onprogress = vi.fn() + await expect(renderer.emit({ + methodName: 'progress', signal: controller.signal, requestTimeout: 100, + }, { onprogress })).resolves.toBe('done') + expect(onprogress.mock.calls).toEqual([['--message-hub-to-be-continued--']]) + controller.abort() + expect(vi.getTimerCount()).toBe(0) + }) + + it.each(['main', 'renderer'])('supports local cancellation and deadlines from the %s process', async (side) => { + const { main, renderer, target } = pair() + const slow = (options: { onprogress: (value: string) => void }) => { + options.onprogress('started') + return new Promise(() => {}) + } + main.on(target, 'slow', slow) + renderer.on('slow', slow) + const emit = (options: { methodName: string; signal?: AbortSignal; requestTimeout: number }, onprogress: (value: string) => void) => side === 'main' + ? main.emit(target, options, { onprogress }) : renderer.emit(options, { onprogress }) + const controller = new AbortController() + const onprogress = vi.fn() + const aborted = expect(emit({ methodName: 'slow', signal: controller.signal, requestTimeout: 100 }, onprogress)) + .rejects.toMatchObject({ code: 6 }) + await vi.advanceTimersByTimeAsync(0) + expect(onprogress).toHaveBeenCalledWith('started') + controller.abort() + await aborted + expect(vi.getTimerCount()).toBe(0) + const timedOut = expect(emit({ methodName: 'slow', requestTimeout: 20 }, onprogress)) + .rejects.toMatchObject({ code: 7 }) + await vi.advanceTimersByTimeAsync(20) + await timedOut + expect(vi.getTimerCount()).toBe(0) + }) + + it('supports calls in both directions and progress across the IPC serialization boundary', async () => { + const { main, renderer, target } = pair() + main.on(target, 'sum', (a: number, b: number) => a + b) + renderer.on('title', () => 'Duplex') + await expect(renderer.emit('sum', 2, 3)).resolves.toBe(5) + await expect(main.emit(target, 'title')).resolves.toBe('Duplex') + main.on('*', 'download', (options: { onprogress: (n: number) => void }) => { + options.onprogress(50) + options.onprogress(100) + return 'done' + }) + const onprogress = vi.fn() + await expect(renderer.emit('download', { onprogress })).resolves.toBe('done') + expect(onprogress.mock.calls).toEqual([[50], [100]]) + expect(vi.getTimerCount()).toBe(0) + }) + + it('removes handlers and isolates custom channels', async () => { + const first = pair('first') + const second = pair('second') + first.main.on('*', 'value', () => 1) + second.main.on('*', 'value', () => 2) + await expect(first.renderer.emit('value')).resolves.toBe(1) + await expect(second.renderer.emit('value')).resolves.toBe(2) + first.main.off('*', 'value') + const missing = expect(first.renderer.emit('value')).rejects.toMatchObject({ code: 3 }) + await vi.advanceTimersByTimeAsync(500) + await missing + }) + + it('rejects pending requests and detaches IPC listeners when destroyed', async () => { + const { main, renderer } = pair() + const pending = expect(renderer.emit('missing')).rejects.toMatchObject({ code: 5 }) + renderer.destroy() + main.destroy() + await pending + expect(transport.ipcMain.listeners.get('message-hub')?.size).toBe(0) + expect(transport.ipcRenderer.listeners.get('message-hub')?.size).toBe(0) + expect(vi.getTimerCount()).toBe(0) + }) + + it('enforces process type and recreates destroyed shared hubs', () => { + inProcess('browser') + expect(() => new RendererMessageHub()).toThrow('renderer process') + const main = MainMessageHub.shared + hubs.push(main) + expect(MainMessageHub.shared).toBe(main) + main.destroy() + hubs.push(MainMessageHub.shared) + expect(MainMessageHub.shared).not.toBe(main) + inProcess('renderer') + expect(() => new MainMessageHub()).toThrow('main process') + const renderer = RendererMessageHub.shared + hubs.push(renderer) + renderer.destroy() + hubs.push(RendererMessageHub.shared) + expect(RendererMessageHub.shared).not.toBe(renderer) + }) +}) diff --git a/packages/simple-electron-ipc/tsconfig.build.json b/packages/simple-electron-ipc/tsconfig.build.json new file mode 100644 index 0000000..75a0c77 --- /dev/null +++ b/packages/simple-electron-ipc/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "emitDeclarationOnly": true + }, + "include": [ + "src" + ] +} diff --git a/packages/simple-electron-ipc/tsconfig.json b/packages/simple-electron-ipc/tsconfig.json index 241b7aa..ebbc976 100644 --- a/packages/simple-electron-ipc/tsconfig.json +++ b/packages/simple-electron-ipc/tsconfig.json @@ -1,28 +1,18 @@ { + "extends": "../../tsconfig.json", "compilerOptions": { - "rootDir": "./", - "outDir": "./dist", - "module": "ESNext", - "target": "ES2017", - "lib": [ - "ES2017", - "DOM" - ], - "strict": true, - "allowJs": false, - "skipLibCheck": true, - "declaration": true, - "noImplicitAny": true, - "moduleResolution": "node", - "esModuleInterop": true, - "noImplicitOverride": true, + "types": [ + "node", + "vite/client" + ] }, "include": [ - "src" + "src", + "test", + "*.mts" ], "exclude": [ "dist", - "vite.config.ts", "demo" ] -} \ No newline at end of file +} diff --git a/packages/simple-electron-ipc/vite.config.mts b/packages/simple-electron-ipc/vite.config.mts new file mode 100644 index 0000000..8d79b51 --- /dev/null +++ b/packages/simple-electron-ipc/vite.config.mts @@ -0,0 +1,32 @@ +import { defineConfig } from 'vite' + +export default defineConfig({ + build: { + target: 'es2018', + lib: { + entry: 'src/index.ts', + formats: ['cjs', 'es'], + fileName: (format) => format === 'cjs' ? 'index.js' : 'index.es.mjs', + }, + rolldownOptions: { + external: ['electron', 'duplex-message'], + output: { + plugins: [{ + name: 'legacy-es-filename', + generateBundle(_options, bundle) { + if (bundle['index.js']) { + this.emitFile({ + type: 'asset', fileName: 'index.node.mjs', + source: "import api from './index.js'\nexport const { MainMessageHub, RendererMessageHub, setConfig, createRpcClient, waitForPeer, READY_METHOD, EErrorCode } = api\n", + }) + } + const chunk = bundle['index.es.mjs'] + if (chunk?.type === 'chunk') { + this.emitFile({ type: 'asset', fileName: 'index.es.js', source: chunk.code }) + } + }, + }], + }, + }, + }, +}) diff --git a/packages/simple-electron-ipc/vite.config.ts b/packages/simple-electron-ipc/vite.config.ts deleted file mode 100644 index 2ba8642..0000000 --- a/packages/simple-electron-ipc/vite.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig } from 'vite' -import dts from 'vite-plugin-dts' - -export default defineConfig(() => ({ - plugins: [dts({ - rollupTypes: true, - })], - build: { - lib: { - entry: 'src/index.ts', - name: 'SimpleElectronIpc', - formats: ['umd', 'es'], - fileName: (format) => (format === 'umd' ? 'index.js' : 'index.es.js'), - }, - rollupOptions: { - external: [ - 'electron', - 'duplex-message', - ], - }, - }, -})) diff --git a/packages/simple-electron-ipc/vitest.config.mts b/packages/simple-electron-ipc/vitest.config.mts new file mode 100644 index 0000000..57a9565 --- /dev/null +++ b/packages/simple-electron-ipc/vitest.config.mts @@ -0,0 +1,5 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { include: ['test/**/*.test.ts'], env: { NODE_ENV: 'production' } }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fc7190d..dd968c7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,3 +1,161 @@ +--- +lockfileVersion: '9.0' + +importers: + + .: + configDependencies: {} + packageManagerDependencies: + pnpm: + specifier: 12.8.1 + version: 12.8.1 + +packages: + + '@pnpm/exe.android-arm64@12.8.1': + resolution: {integrity: sha512-siDxcidfz5eM6L2tJfik+i2bbUEw8UC7MZ3Yl2HYdRmqbdhHGAOQACtOOu6QVW4Jul4/w5wVKm+wM8ACtgh1Pg==} + cpu: [arm64] + os: [android] + + '@pnpm/exe.android-x64@12.8.1': + resolution: {integrity: sha512-0F/uqPRc3aN4vLHsa0f+6fTtmsigFQ9/yxEU43fionF//pNHEd5e97b3w/nKIgqYMCUQCJphmBRMA9Q8/wlo9Q==} + cpu: [x64] + os: [android] + + '@pnpm/exe.darwin-arm64@12.8.1': + resolution: {integrity: sha512-/rwavvMQJsl2RIxOHBnDF+4Gk+fhQFAY4M3PQZoKskJOEnaJcygG9p1xby6wcQcbS4d9dIubv09CQhufTgfmpw==} + cpu: [arm64] + os: [darwin] + + '@pnpm/exe.darwin-x64@12.8.1': + resolution: {integrity: sha512-htYt2gN7zqJKLhC99Ft6EUiO7myjZAZXfmGCTJUL1v+o7vE9hZdE1+fAkk4Oj1ZHNRti6b2woOxEkRkAusi8OQ==} + cpu: [x64] + os: [darwin] + + '@pnpm/exe.freebsd-x64@12.8.1': + resolution: {integrity: sha512-PB5BULNgytxvExoOXUUeseS/DcpPENs+cg3iA6xHHeaLT4ctEGKSGXEbuj7USeqvo8JBpAx039hivnbzGqDwnA==} + cpu: [x64] + os: [freebsd] + + '@pnpm/exe.linux-arm64-musl@12.8.1': + resolution: {integrity: sha512-kKeyC/ArjrgdciQRBrOL0j9HtOI4gDRgoyP+eJ1CQKyadpKlf/DhDDyspXe07R3B9KagTVLUF2nlt6Dr5quFIg==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-arm64@12.8.1': + resolution: {integrity: sha512-q4s9a9X2O3N9aeUFooW24orNrxvu20+jyVUFR5RanPCqOKKPBrgs2iywMkBBx1aXkkzdWT54/mfz8Be8sJlWEw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-ppc64@12.8.1': + resolution: {integrity: sha512-F7XSjKJthwCh/L6abZiMpAfo2Cm61SZIT3e4dfgiIjESndlWaXCnfLKngSgZ/SEMsdmHT6dud3+kWIB3tw0/xQ==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-riscv64@12.8.1': + resolution: {integrity: sha512-z1ecpIK/ZY08Unk69KWWE9867oaKUytGUjrHnBoGFAAhsK+q+0cfvBH+f5fWobJ7FX97leMlLO+gkJolddM45Q==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-s390x@12.8.1': + resolution: {integrity: sha512-KU6tnrBfu4uKXTq6N5qJwSm8FSrs9Wc8b5AUEsHK9bRXwNdsZ1/lRxG1++X29SKRW9OSNuDSjxASv5mAvGo1gA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@pnpm/exe.linux-x64-musl@12.8.1': + resolution: {integrity: sha512-eUE8BWJkDr2tJb/yrk0yVnszlmNqFarVxUlqiZ9BAkct28Nq/5g2H0tvMDmPA7fAQW3STNj+omJPPRVT+4s5sQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@pnpm/exe.linux-x64@12.8.1': + resolution: {integrity: sha512-8bDZ0lZlCdi2rvnFul7EYgcC7zKeWSe76y8JV2oXobaS8p9i9d6PSf6LgLtTM0fI7R9Oh4eLCbveXyNDMmvZ+g==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@pnpm/exe.win32-arm64@12.8.1': + resolution: {integrity: sha512-MsT0dlrRxnCRCHCaosNbhWpEiAFnFuMEu3KuDJDU41BXgTwiINxU8goloUSUDejWZUqKaUqNC/qUTihl9nFtqg==} + cpu: [arm64] + os: [win32] + + '@pnpm/exe.win32-x64@12.8.1': + resolution: {integrity: sha512-SWtPe0PsbTTk//gJND10tMdOfCYjrkq5+qV49hzVhY7xz2iJ339Rc+xwASlcvbwTfrvH39YNqpKYibD+CRGLwA==} + cpu: [x64] + os: [win32] + + pnpm@12.8.1: + resolution: {integrity: sha512-9kupB1B/XOr+BsjTjmBS0BeURFgOwSed3Vv8EctIqoomRLZlmOB+dh2oSHKp/FfV+QK4f6SdAkGY1VhhKqu+RQ==} + engines: {node: '>=18.*'} + hasBin: true + +snapshots: + + '@pnpm/exe.android-arm64@12.8.1': + optional: true + + '@pnpm/exe.android-x64@12.8.1': + optional: true + + '@pnpm/exe.darwin-arm64@12.8.1': + optional: true + + '@pnpm/exe.darwin-x64@12.8.1': + optional: true + + '@pnpm/exe.freebsd-x64@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-arm64@12.8.1': + optional: true + + '@pnpm/exe.linux-ppc64@12.8.1': + optional: true + + '@pnpm/exe.linux-riscv64@12.8.1': + optional: true + + '@pnpm/exe.linux-s390x@12.8.1': + optional: true + + '@pnpm/exe.linux-x64-musl@12.8.1': + optional: true + + '@pnpm/exe.linux-x64@12.8.1': + optional: true + + '@pnpm/exe.win32-arm64@12.8.1': + optional: true + + '@pnpm/exe.win32-x64@12.8.1': + optional: true + + pnpm@12.8.1: + optionalDependencies: + '@pnpm/exe.android-arm64': 12.8.1 + '@pnpm/exe.android-x64': 12.8.1 + '@pnpm/exe.darwin-arm64': 12.8.1 + '@pnpm/exe.darwin-x64': 12.8.1 + '@pnpm/exe.freebsd-x64': 12.8.1 + '@pnpm/exe.linux-arm64': 12.8.1 + '@pnpm/exe.linux-arm64-musl': 12.8.1 + '@pnpm/exe.linux-ppc64': 12.8.1 + '@pnpm/exe.linux-riscv64': 12.8.1 + '@pnpm/exe.linux-s390x': 12.8.1 + '@pnpm/exe.linux-x64': 12.8.1 + '@pnpm/exe.linux-x64-musl': 12.8.1 + '@pnpm/exe.win32-arm64': 12.8.1 + '@pnpm/exe.win32-x64': 12.8.1 + +--- lockfileVersion: '9.0' settings: @@ -6,279 +164,190 @@ settings: catalogs: default: + '@vitest/browser-playwright': + specifier: 5.0.3 + version: 5.0.3 + '@vitest/coverage-v8': + specifier: 5.0.3 + version: 5.0.3 electron: - specifier: ^31.0.0 - version: 31.3.1 + specifier: 44.5.1 + version: 44.5.1 + playwright: + specifier: 1.63.0 + version: 1.63.0 + typescript: + specifier: 7.0.2 + version: 7.0.2 vite: - specifier: ^5.3.3 - version: 5.3.3 - vite-plugin-dts: - specifier: ^4.0.2 - version: 4.0.3 + specifier: 8.3.2 + version: 8.3.2 vitest: - specifier: ^2.0.0 - version: 2.0.2 - -overrides: - typescript: 5.5.3 + specifier: 5.0.3 + version: 5.0.3 importers: .: devDependencies: '@changesets/cli': - specifier: ^2.29.2 - version: 2.29.2 - '@typescript-eslint/eslint-plugin': - specifier: ^4.22.1 - version: 4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0)(typescript@5.5.3) - '@typescript-eslint/parser': - specifier: ^4.22.1 - version: 4.33.0(eslint@7.32.0)(typescript@5.5.3) - cross-env: - specifier: ^5.2.0 - version: 5.2.1 - eslint: - specifier: ^7.25.0 - version: 7.32.0 - eslint-config-airbnb-typescript: - specifier: ^12.3.1 - version: 12.3.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.34.3(eslint@7.32.0))(eslint@7.32.0)(typescript@5.5.3) - eslint-plugin-import: - specifier: ^2.22.0 - version: 2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0) - eslint-plugin-jsx-a11y: - specifier: ^6.3.1 - version: 6.9.0(eslint@7.32.0) - eslint-plugin-react: - specifier: ^7.20.3 - version: 7.34.3(eslint@7.32.0) - eslint-plugin-react-hooks: - specifier: ^4.0.8 - version: 4.6.2(eslint@7.32.0) - husky: - specifier: ^1.1.3 - version: 1.3.1 + specifier: 3.0.3 + version: 3.0.3 + '@types/node': + specifier: ^24.0.0 + version: 24.19.1 + acorn: + specifier: 8.18.0 + version: 8.18.0 + oxlint: + specifier: 1.86.0 + version: 1.86.0 + typescript: + specifier: 'catalog:' + version: 7.0.2 packages/duplex-message: devDependencies: - '@vitest/browser': - specifier: ^2.0.2 - version: 2.0.2(playwright@1.45.1)(typescript@5.5.3)(vitest@2.0.2)(webdriverio@8.40.2) - '@vitest/coverage-istanbul': - specifier: ^2.0.2 - version: 2.0.2(vitest@2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0)) + '@vitest/browser-playwright': + specifier: 'catalog:' + version: 5.0.3(playwright@1.63.0)(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3) + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 5.0.3(@vitest/browser@5.0.3)(vitest@5.0.3) playwright: - specifier: ^1.45.1 - version: 1.45.1 - tsx: - specifier: ^4.17.0 - version: 4.17.0 + specifier: 'catalog:' + version: 1.63.0 typescript: - specifier: 5.5.3 - version: 5.5.3 - vite: specifier: 'catalog:' - version: 5.3.3(@types/node@22.3.0) - vite-plugin-dts: + version: 7.0.2 + vite: specifier: 'catalog:' - version: 4.0.3(@types/node@22.3.0)(rollup@4.18.1)(typescript@5.5.3)(vite@5.3.3(@types/node@22.3.0)) + version: 8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1) vitest: specifier: 'catalog:' - version: 2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0) + version: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) packages/simple-electron-ipc: dependencies: duplex-message: - specifier: 'workspace:' + specifier: workspace:^ version: link:../duplex-message devDependencies: electron: specifier: 'catalog:' - version: 31.3.1 + version: 44.5.1(supports-color@8.1.1) typescript: - specifier: 5.5.3 - version: 5.5.3 - vite: specifier: 'catalog:' - version: 5.3.3(@types/node@22.3.0) - vite-plugin-dts: + version: 7.0.2 + vite: specifier: 'catalog:' - version: 4.0.3(@types/node@22.3.0)(rollup@4.18.1)(typescript@5.5.3)(vite@5.3.3(@types/node@22.3.0)) + version: 8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1) vitest: specifier: 'catalog:' - version: 2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0) + version: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@babel/code-frame@7.12.11': - resolution: {integrity: sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==} - - '@babel/code-frame@7.24.7': - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.24.8': - resolution: {integrity: sha512-c4IM7OTg6k1Q+AJ153e2mc2QVTezTwnb4VzquwcyiEzGnW0Kedv4do/TrkU98qPeC5LNiMt/QXwIjzYXLBpyZg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.24.8': - resolution: {integrity: sha512-6AWcmZC/MZCO0yKys4uhg5NlxL0ESF3K6IAaoQ+xSXvPyPyxNWRafP+GDbI88Oh68O7QkJgmEtedWPM9U0pZNg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.24.8': - resolution: {integrity: sha512-47DG+6F5SzOi0uEvK4wMShmn5yY0mVjVJoWTphdY2B4Rx9wHgjK7Yhtr0ru6nE+sn0v38mzrWOlah0p/YlHHOQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.24.8': - resolution: {integrity: sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-environment-visitor@7.24.7': - resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-function-name@7.24.7': - resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-hoist-variables@7.24.7': - resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.24.7': - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.24.8': - resolution: {integrity: sha512-m4vWKVqvkVAWLXfHCCfff2luJj86U+J0/x+0N3ArG/tP0Fq7zky2dYwMbtPmkc/oulkkbjdL3uWzuoBwQ8R00Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-simple-access@7.24.7': - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-split-export-declaration@7.24.7': - resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.24.8': - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.24.7': - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.24.8': - resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.24.8': - resolution: {integrity: sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.7': - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.8': - resolution: {integrity: sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==} + '@babel/parser@7.29.9': + resolution: {integrity: sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.24.8': - resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.24.7': - resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.24.8': - resolution: {integrity: sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.24.8': - resolution: {integrity: sha512-SkSBEHwwJRU52QEVZBmMBnE5Ux2/6WU1grdYyOhpbCNxbmJrDuDCphBzKZSO3taf0zztp+qkWlymE5tVL5l0TA==} + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} engines: {node: '>=6.9.0'} - '@bundled-es-modules/cookie@2.0.0': - resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} - '@bundled-es-modules/statuses@1.0.1': - resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + '@blazediff/core@1.10.0': + resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==} - '@changesets/apply-release-plan@7.0.12': - resolution: {integrity: sha512-EaET7As5CeuhTzvXTQCRZeBUcisoYPDDcXvgTE/2jmmypKp0RC7LxKj/yzqeh/1qFTZI7oDGFcL1PHRuQuketQ==} + '@changesets/apply-release-plan@8.1.1': + resolution: {integrity: sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/assemble-release-plan@6.0.6': - resolution: {integrity: sha512-Frkj8hWJ1FRZiY3kzVCKzS0N5mMwWKwmv9vpam7vt8rZjLL1JMthdh6pSDVSPumHPshTTkKZ0VtNbE0cJHZZUg==} + '@changesets/assemble-release-plan@7.0.0': + resolution: {integrity: sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/changelog-git@0.2.1': - resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + '@changesets/changelog-git@1.0.0': + resolution: {integrity: sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/cli@2.29.2': - resolution: {integrity: sha512-vwDemKjGYMOc0l6WUUTGqyAWH3AmueeyoJa1KmFRtCYiCoY5K3B68ErYpDB6H48T4lLI4czum4IEjh6ildxUeg==} + '@changesets/cli@3.0.3': + resolution: {integrity: sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q==} + engines: {node: ^22.11 || ^24 || >=26, npm: '>=10.9.0', pnpm: '>=10.0.0', yarn: '>=4.5.2'} hasBin: true - '@changesets/config@3.1.1': - resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + '@changesets/config@4.0.1': + resolution: {integrity: sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/errors@0.2.0': - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + '@changesets/errors@1.0.0': + resolution: {integrity: sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-dependents-graph@2.1.3': - resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + '@changesets/format@0.1.2': + resolution: {integrity: sha512-Caez5XtNXCFS/G5bwyav3wuXL0tMxVd2ZGbaumWbzN08tyzO21asCw7JZhNtVsAZDCvDRUzZN+Iit9SyRITSYA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-release-plan@4.0.10': - resolution: {integrity: sha512-CCJ/f3edYaA3MqoEnWvGGuZm0uMEMzNJ97z9hdUR34AOvajSwySwsIzC/bBu3+kuGDsB+cny4FljG8UBWAa7jg==} + '@changesets/get-dependents-graph@3.0.0': + resolution: {integrity: sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/get-version-range-type@0.4.0': - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + '@changesets/git@4.0.1': + resolution: {integrity: sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/git@3.0.4': - resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + '@changesets/parse@1.0.0': + resolution: {integrity: sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/logger@0.1.1': - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + '@changesets/pre@3.0.0': + resolution: {integrity: sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/parse@0.4.1': - resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + '@changesets/read@1.0.1': + resolution: {integrity: sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/pre@2.0.2': - resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + '@changesets/should-skip-package@1.0.0': + resolution: {integrity: sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/read@0.6.5': - resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + '@changesets/types@7.0.0': + resolution: {integrity: sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/should-skip-package@0.1.2': - resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + '@changesets/write@1.0.1': + resolution: {integrity: sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==} + engines: {node: ^22.11 || ^24 || >=26} - '@changesets/types@4.1.0': - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + '@clack/core@1.5.1': + resolution: {integrity: sha512-iHTrHA8MtVuLl2TfZySmcKv1qO2PoyC9Z7pfSDozEuV5vtY3/wcOPKJXlqJ5Oq2Cx5DDGQGAMVx6HZfRRoVEbQ==} + engines: {node: '>= 20.12.0'} - '@changesets/types@6.1.0': - resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + '@clack/prompts@1.8.1': + resolution: {integrity: sha512-dlT1m5e/0yUL0kRNcQn7yGLVThkgbB0Ga/1AmfDDC/8ik6AIiSf2QLQO2zPYvefsHP0aFgxO93cVLCCfDp7kzQ==} + engines: {node: '>= 20.12.0'} - '@changesets/write@0.4.0': - resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - - '@electron/get@2.0.3': - resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} - engines: {node: '>=12'} + '@electron-internal/extract-zip@1.0.5': + resolution: {integrity: sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==} + engines: {node: '>=22.12.0'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] + '@electron/get@5.1.0': + resolution: {integrity: sha512-3kSBtG8ObcTVfXanm5vVJ6UnBLEVmVsRk1M+vGqCuMBV+XLCbJYuWQful+yIy0GQDsSlK0kHEriEHn7SPk4EnA==} + engines: {node: '>=22.12.0'} '@esbuild/aix-ppc64@0.23.0': resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} @@ -286,204 +355,102 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.23.0': resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.23.0': resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.23.0': resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.23.0': resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.23.0': resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.23.0': resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.23.0': resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.23.0': resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.23.0': resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.23.0': resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.23.0': resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.23.0': resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.23.0': resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.23.0': resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.23.0': resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.23.0': resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} engines: {node: '>=18'} @@ -496,7638 +463,2119 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.23.0': resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.23.0': resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.23.0': resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.23.0': resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.23.0': resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint/eslintrc@0.4.3': - resolution: {integrity: sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==} - engines: {node: ^10.12.0 || >=12.0.0} - - '@humanwhocodes/config-array@0.5.0': - resolution: {integrity: sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead - - '@humanwhocodes/object-schema@1.2.1': - resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} - deprecated: Use @eslint/object-schema instead - - '@inquirer/confirm@3.1.14': - resolution: {integrity: sha512-nbLSX37b2dGPtKWL3rPuR/5hOuD30S+pqJ/MuFiUEgN6GiMs8UMxiurKAMDzKt6C95ltjupa8zH6+3csXNHWpA==} - engines: {node: '>=18'} - - '@inquirer/core@9.0.2': - resolution: {integrity: sha512-nguvH3TZar3ACwbytZrraRTzGqyxJfYJwv+ZwqZNatAosdWQMP1GV8zvmkNlBe2JeZSaw0WYBHZk52pDpWC9qA==} - engines: {node: '>=18'} - - '@inquirer/figures@1.0.3': - resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} - engines: {node: '>=18'} - - '@inquirer/type@1.4.0': - resolution: {integrity: sha512-AjOqykVyjdJQvtfkNDGUyMYGF8xN50VUxftCQWsOyIo4DFRLr6VQhW0VItGI1JIyQGCGgIpKa7hMMwNhZb4OIw==} - engines: {node: '>=18'} - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} - engines: {node: '>=8'} - - '@jridgewell/gen-mapping@0.3.5': - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} - '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} - '@manypkg/find-root@1.1.0': - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@manypkg/get-packages@1.1.3': - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} - '@microsoft/api-extractor-model@7.29.4': - resolution: {integrity: sha512-LHOMxmT8/tU1IiiiHOdHFF83Qsi+V8d0kLfscG4EvQE9cafiR8blOYr8SfkQKWB1wgEilQgXJX3MIA4vetDLZw==} + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} - '@microsoft/api-extractor@7.47.4': - resolution: {integrity: sha512-HKm+P4VNzWwvq1Ey+Jfhhj/3MjsD+ka2hbt8L5AcRM95lu1MFOYnz3XlU7Gr79Q/ZhOb7W/imAKeYrOI0bFydg==} - hasBin: true + '@manypkg/tools@2.1.2': + resolution: {integrity: sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==} + engines: {node: '>=20.0.0'} - '@microsoft/tsdoc-config@0.17.0': - resolution: {integrity: sha512-v/EYRXnCAIHxOHW+Plb6OWuUoMotxTN0GLatnpOb1xq0KuTNw/WI3pamJx/UbsoJP5k9MCw1QxvvhPcF9pH3Zg==} + '@oxc-project/types@0.152.0': + resolution: {integrity: sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==} - '@microsoft/tsdoc@0.15.0': - resolution: {integrity: sha512-HZpPoABogPvjeJOdzCOSJsXeL/SMCBgBZMVC3X3d7YYp2gf31MfxhUoYUNwf1ERPJOnQc0wkFn9trqI6ZEdZuA==} + '@oxlint/binding-android-arm-eabi@1.86.0': + resolution: {integrity: sha512-63Ozq6yQn79B2UxjSZ9huJmI+3/ZPI7hxAe6TyFh9oQL447KnIMoffOdtog+h4KILbQr61ZRvFPW5gYbAUgpgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] - '@mswjs/cookies@1.1.1': - resolution: {integrity: sha512-W68qOHEjx1iD+4VjQudlx26CPIoxmIAtK4ZCexU0/UJBG6jYhcuyzKJx+Iw8uhBIGd9eba64XgWVgo20it1qwA==} - engines: {node: '>=18'} + '@oxlint/binding-android-arm64@1.86.0': + resolution: {integrity: sha512-JdLGp44phbf/0Du236Dt06yfK6Qw8xhTFeDvHJdjk167r59OfP5+BL31mgMYVnswS8e+j6tFuccSY0QDC/lgCw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] - '@mswjs/interceptors@0.29.1': - resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==} - engines: {node: '>=18'} + '@oxlint/binding-darwin-arm64@1.86.0': + resolution: {integrity: sha512-h+vkOr4ik6KLFCXdtNVEj9xfnXfNUpPoF95QjORdhiSpLqQePzcN81kGHjAgsw1/G7WBWK5KsFKbI2UK8EyzMA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@oxlint/binding-darwin-x64@1.86.0': + resolution: {integrity: sha512-wbglRuH5eKsp68keTOOeKDHU1astWSRYFsh2bwKQTAPb44rD6kVgzYI1ZrAJRTc6/7PH55ZXFCQx0rQXrj0UdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@oxlint/binding-freebsd-x64@1.86.0': + resolution: {integrity: sha512-yTQ7QSOlNfQsrfbeVweTiegQJLKPjUKgXp/JpGiFzza6Hp29S9LAoH77TM3L0RjK39iQRZB5lk5iq2t241Mfag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': + resolution: {integrity: sha512-QYJGy0E2Tv8GAayyBIeHFTwQb8u+zrvrOYrifuDGZDr9ad5XsDhiMScpZymhS9fwWjqrgG2ZUwT4vpY0ISqp4Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@open-draft/deferred-promise@2.2.0': - resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + '@oxlint/binding-linux-arm-musleabihf@1.86.0': + resolution: {integrity: sha512-oQo83CafLT8p3qh0AGkN0tSUIh5Hm63mfDsoqUmBQUBnSZLNBAbyRaAftfGY5a0cKZ34dG6jAtMYgcY5kZqt5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] - '@open-draft/logger@0.3.0': - resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + '@oxlint/binding-linux-arm64-gnu@1.86.0': + resolution: {integrity: sha512-EM6wy5c2UM12qPb7iDJDBAZvHyfEJGH6iosFgaFiaRZpirz99//EHcUyD2KAskz07x1TULuiAtsojf4nvWWksw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@open-draft/until@2.1.0': - resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@oxlint/binding-linux-arm64-musl@1.86.0': + resolution: {integrity: sha512-vCiUQb9ZNzZalxYRU4mlZwUqT5f+c7Sk9DwUOplWHtik3Dl4r3DVnUB5tBeRuTAUe6MUVCTylNW7TkkN8J1oTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} + '@oxlint/binding-linux-ppc64-gnu@1.86.0': + resolution: {integrity: sha512-T0AzE89yjTYmOhB/hb6i+rWTpe2mYt+xiguBhs9DEPIJTZ1H8OYqsE4VxJHHw2Wlq8Mztwvp2P8NV87ZOuhPRw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] - '@polka/url@1.0.0-next.25': - resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + '@oxlint/binding-linux-riscv64-gnu@1.86.0': + resolution: {integrity: sha512-s49r0K2f5Oc7fJoMUO1WkdhYK5Ob79uYRwWQ/XNoGysaOq3RUGmGUbHSuXiyydTTA+P9xEdDEnnlZVXiaSJhlw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] - '@promptbook/utils@0.63.0': - resolution: {integrity: sha512-/E3sYjbaZ/7DRK2Uf0KayjKstza23ckJsYbSN9M4fPZa5dasW8F2tL6Gd3g2m5wYkjwuwtyc9JsOLNOFaxu+8w==} + '@oxlint/binding-linux-riscv64-musl@1.86.0': + resolution: {integrity: sha512-JIa2tYR8ayb3nvcMSZzn4O/xTy13S7MHllu7cBD+B5qDQXXSRy/Vwk1aNpiTjZmBJGwKCHOy/NvnY5Xcwg27uQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] - '@puppeteer/browsers@1.9.1': - resolution: {integrity: sha512-PuvK6xZzGhKPvlx3fpfdM2kYY3P/hB1URtK8wA7XUJ6prn6pp22zvJHu48th0SGcHL9SutbPHrFuQgfXTFobWA==} - engines: {node: '>=16.3.0'} - hasBin: true + '@oxlint/binding-linux-s390x-gnu@1.86.0': + resolution: {integrity: sha512-XwXMyBsAuWZkhZU74UNcfQKr1CJJAKnS80dZzOnjl6A4fKcf91IjFgidegQugMHA5SN8ulwDXEDV1yPntjXgQQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] - '@rollup/pluginutils@5.1.0': - resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@oxlint/binding-linux-x64-gnu@1.86.0': + resolution: {integrity: sha512-C1WjukSyMnr66b+w1/tV8RFVv6d9v0MzDf4p9IxVXknqgmTHBgZh1pccN1eHzFr0b9Tbb3OXoPsAAdAuHAQfeA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] - '@rollup/rollup-android-arm-eabi@4.18.1': - resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} + '@oxlint/binding-linux-x64-musl@1.86.0': + resolution: {integrity: sha512-ap6KLmvC38c6MdYzsIh25cXQupqYvjd37tMNftzrX1DCtkX1Gcf2B+S2B17dD4lKa5c5gJog7DQJyDo82PBKyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.86.0': + resolution: {integrity: sha512-996Q9w/2GLwNyy5Djs39Jt09aFlr4b7TPhoj7dvo5FHmcyiF68nPz73HNc5UAYKIYeK+M9JzPQ4Wg1r1dXvP6A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.86.0': + resolution: {integrity: sha512-9uTCHYTknNkug5+46ViSoUMuOuGWK61D87QnblU6CXaWKJC630Z7dbls61vdHxya/cR2ycQZ6Mm6ckgF6Cag5g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.86.0': + resolution: {integrity: sha512-a47ga7EKVfMU8rO0i1m3mGwfNRj3FhQyt1d/ukO/E9u1HHF3KpR3tyb3VhuJBGEVPF8uR4exSyHUyx7xbE+uxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.86.0': + resolution: {integrity: sha512-az4dzifCRzES1crwu6pDFXzJTIanrdxXFL9TbiJWrx9LKcuacbR83aPvxDA1LKLNswi/RoqitC7Afcflhu3zBQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@pnpm/deps.graph-sequencer@1100.0.1': + resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==} + engines: {node: '>=22.13'} + + '@polka/url@1.0.0-next.25': + resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==} + + '@rolldown/binding-android-arm-eabi@1.2.12': + resolution: {integrity: sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.1': - resolution: {integrity: sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==} + '@rolldown/binding-android-arm64@1.2.12': + resolution: {integrity: sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.1': - resolution: {integrity: sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==} + '@rolldown/binding-darwin-arm64@1.2.12': + resolution: {integrity: sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.1': - resolution: {integrity: sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==} + '@rolldown/binding-darwin-x64@1.2.12': + resolution: {integrity: sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.18.1': - resolution: {integrity: sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==} - cpu: [arm] - os: [linux] + '@rolldown/binding-freebsd-x64@1.2.12': + resolution: {integrity: sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] - '@rollup/rollup-linux-arm-musleabihf@4.18.1': - resolution: {integrity: sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==} + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + resolution: {integrity: sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.1': - resolution: {integrity: sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==} + '@rolldown/binding-linux-arm64-gnu@1.2.12': + resolution: {integrity: sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.18.1': - resolution: {integrity: sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==} + '@rolldown/binding-linux-arm64-musl@1.2.12': + resolution: {integrity: sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] + libc: [musl] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': - resolution: {integrity: sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==} + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + resolution: {integrity: sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.18.1': - resolution: {integrity: sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==} - cpu: [riscv64] - os: [linux] - - '@rollup/rollup-linux-s390x-gnu@4.18.1': - resolution: {integrity: sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==} + '@rolldown/binding-linux-s390x-gnu@1.2.12': + resolution: {integrity: sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.18.1': - resolution: {integrity: sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==} + '@rolldown/binding-linux-x64-gnu@1.2.12': + resolution: {integrity: sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.18.1': - resolution: {integrity: sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==} + '@rolldown/binding-linux-x64-musl@1.2.12': + resolution: {integrity: sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] + libc: [musl] - '@rollup/rollup-win32-arm64-msvc@4.18.1': - resolution: {integrity: sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==} + '@rolldown/binding-openharmony-arm64@1.2.12': + resolution: {integrity: sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] - os: [win32] + os: [openharmony] - '@rollup/rollup-win32-ia32-msvc@4.18.1': - resolution: {integrity: sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==} - cpu: [ia32] + '@rolldown/binding-win32-arm64-msvc@1.2.12': + resolution: {integrity: sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.1': - resolution: {integrity: sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==} + '@rolldown/binding-win32-x64-msvc@1.2.12': + resolution: {integrity: sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==} + engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rushstack/node-core-library@5.5.1': - resolution: {integrity: sha512-ZutW56qIzH8xIOlfyaLQJFx+8IBqdbVCZdnj+XT1MorQ1JqqxHse8vbCpEM+2MjsrqcbxcgDIbfggB1ZSQ2A3g==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - - '@rushstack/rig-package@0.5.3': - resolution: {integrity: sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==} - - '@rushstack/terminal@0.13.3': - resolution: {integrity: sha512-fc3zjXOw8E0pXS5t9vTiIPx9gHA0fIdTXsu9mT4WbH+P3mYvnrX0iAQ5a6NvyK1+CqYWBTw/wVNx7SDJkI+WYQ==} - peerDependencies: - '@types/node': '*' - peerDependenciesMeta: - '@types/node': - optional: true - - '@rushstack/ts-command-line@4.22.3': - resolution: {integrity: sha512-edMpWB3QhFFZ4KtSzS8WNjBgR4PXPPOVrOHMbb7kNpmQ1UFS9HdVtjCXg1H5fG+xYAbeE+TMPcVPUyX2p84STA==} - - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - - '@sindresorhus/is@5.6.0': - resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} - engines: {node: '>=14.16'} + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@testing-library/dom@10.3.1': - resolution: {integrity: sha512-q/WL+vlXMpC0uXDyfsMtc1rmotzLV8Y0gq6q1gfrrDjQeHoeLrqHbxdPvPNAh1i+xuJl7+BezywcXArz7vLqKQ==} - engines: {node: '>=18'} + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - '@testing-library/user-event@14.5.2': - resolution: {integrity: sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==} - engines: {node: '>=12', npm: '>=6'} - peerDependencies: - '@testing-library/dom': '>=7.21.4' + '@types/node@24.19.1': + resolution: {integrity: sha512-aS3/DG0oM05K0RIXXP+hKjinGG5IgSSVGzswZxW3O0sS3pH4/fycXundUC9XsszgKCk4gHXylTEK6hyFxVxnoQ==} - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} + '@typescript/typescript-aix-ppc64@7.0.2': + resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [aix] - '@types/argparse@1.0.38': - resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@typescript/typescript-darwin-arm64@7.0.2': + resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [darwin] - '@types/aria-query@5.0.4': - resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@typescript/typescript-darwin-x64@7.0.2': + resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [darwin] - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} + '@typescript/typescript-freebsd-arm64@7.0.2': + resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [freebsd] - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@typescript/typescript-freebsd-x64@7.0.2': + resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [freebsd] - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@typescript/typescript-linux-arm64@7.0.2': + resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [linux] - '@types/http-cache-semantics@4.0.4': - resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + '@typescript/typescript-linux-arm@7.0.2': + resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} + engines: {node: '>=16.20.0'} + cpu: [arm] + os: [linux] - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@typescript/typescript-linux-loong64@7.0.2': + resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} + engines: {node: '>=16.20.0'} + cpu: [loong64] + os: [linux] - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + '@typescript/typescript-linux-mips64el@7.0.2': + resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} + engines: {node: '>=16.20.0'} + cpu: [mips64el] + os: [linux] - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} + '@typescript/typescript-linux-ppc64@7.0.2': + resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} + engines: {node: '>=16.20.0'} + cpu: [ppc64] + os: [linux] - '@types/mute-stream@0.0.4': - resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + '@typescript/typescript-linux-riscv64@7.0.2': + resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} + engines: {node: '>=16.20.0'} + cpu: [riscv64] + os: [linux] - '@types/node@12.20.55': - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@typescript/typescript-linux-s390x@7.0.2': + resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} + engines: {node: '>=16.20.0'} + cpu: [s390x] + os: [linux] - '@types/node@20.14.10': - resolution: {integrity: sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==} + '@typescript/typescript-linux-x64@7.0.2': + resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [linux] - '@types/node@22.3.0': - resolution: {integrity: sha512-nrWpWVaDZuaVc5X84xJ0vNrLvomM205oQyLsRt7OHNZbSHslcWsvgFR7O7hire2ZonjLrWBbedmotmIlJDVd6g==} + '@typescript/typescript-netbsd-arm64@7.0.2': + resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [netbsd] - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} + '@typescript/typescript-netbsd-x64@7.0.2': + resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [netbsd] - '@types/statuses@2.0.5': - resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + '@typescript/typescript-openbsd-arm64@7.0.2': + resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [openbsd] - '@types/which@2.0.2': - resolution: {integrity: sha512-113D3mDkZDjo+EeUEHCFy0qniNc1ZpecGiAU7WSo7YDoSzolZIQKpYFHrPpjkB2nuyahcKfrmLXeQlh7gqJYdw==} + '@typescript/typescript-openbsd-x64@7.0.2': + resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [openbsd] - '@types/wrap-ansi@3.0.0': - resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + '@typescript/typescript-sunos-x64@7.0.2': + resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [sunos] - '@types/ws@8.5.12': - resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==} + '@typescript/typescript-win32-arm64@7.0.2': + resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} + engines: {node: '>=16.20.0'} + cpu: [arm64] + os: [win32] - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} + '@typescript/typescript-win32-x64@7.0.2': + resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} + engines: {node: '>=16.20.0'} + cpu: [x64] + os: [win32] - '@typescript-eslint/eslint-plugin@4.33.0': - resolution: {integrity: sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==} - engines: {node: ^10.12.0 || >=12.0.0} + '@vitest/browser-playwright@5.0.3': + resolution: {integrity: sha512-rYBHwIFL/ZtI6dl0rbrAatScXCrvX+TL2BX2AGOigkC1Y6u3rIWw8KMQJJMS4drSa1ksMCmZU2wpgwdE7kqu3g==} peerDependencies: - '@typescript-eslint/parser': ^4.0.0 - eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + playwright: '*' + vitest: 5.0.3 - '@typescript-eslint/experimental-utils@4.33.0': - resolution: {integrity: sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==} - engines: {node: ^10.12.0 || >=12.0.0} + '@vitest/browser@5.0.3': + resolution: {integrity: sha512-7KHIkVA/gTRxQV9qPa9qLK+g5INZG6iRrlwXSFG5EKsOomW3UzzW7wsdt1Yp5PufzQ43lniuONlb4lUVCC2qSg==} peerDependencies: - eslint: '*' + vitest: 5.0.3 - '@typescript-eslint/parser@4.33.0': - resolution: {integrity: sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==} - engines: {node: ^10.12.0 || >=12.0.0} + '@vitest/coverage-v8@5.0.3': + resolution: {integrity: sha512-+klsyz7BvT1vCU28Zkfzms1Ia78XD0V41U3FUtRaa3S+vOr/EXvK1O6BvVD7l1RzEjm6SONR2aybOvDDf9u0mQ==} peerDependencies: - eslint: ^5.0.0 || ^6.0.0 || ^7.0.0 - typescript: '*' + '@vitest/browser': 5.0.3 + vitest: 5.0.3 peerDependenciesMeta: - typescript: + '@vitest/browser': optional: true - '@typescript-eslint/scope-manager@4.33.0': - resolution: {integrity: sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - - '@typescript-eslint/types@4.33.0': - resolution: {integrity: sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} + '@vitest/istanbul-lib-coverage@1.0.2': + resolution: {integrity: sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==} + engines: {node: '>=22'} - '@typescript-eslint/typescript-estree@4.33.0': - resolution: {integrity: sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@vitest/istanbul-lib-report@1.0.2': + resolution: {integrity: sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==} + engines: {node: '>=22'} - '@typescript-eslint/visitor-keys@4.33.0': - resolution: {integrity: sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==} - engines: {node: ^8.10.0 || ^10.13.0 || >=11.10.1} - - '@vitest/browser@2.0.2': - resolution: {integrity: sha512-0U0LY7Yq5INXwS1+CDlKEntgtWGYu5pdVmuzD9alXEBDeNnjMD0fKL4dO7V7uIWnzMgzVjKxE205y4IoRBPn/A==} + '@vitest/mocker@5.0.3': + resolution: {integrity: sha512-T8sWAIbkSyAjkwTcaEc3Iu0o9A27X1/kdXrizhZkGuSKScRQtRzclfAMpOTcGdXCsqxeWlpGy3XjqaW8CpLORg==} peerDependencies: - playwright: '*' - safaridriver: '*' - vitest: 2.0.2 - webdriverio: '*' + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - playwright: - optional: true - safaridriver: + msw: optional: true - webdriverio: + vite: optional: true - '@vitest/coverage-istanbul@2.0.2': - resolution: {integrity: sha512-9TZC/4CT9j7GZYwh1fYtxNtRoSi7T4evF5M/rxOOAvgejJFxM/ysXSvdyV/HXWkEbH/Be8uKnd+v/TyYyrglGA==} - peerDependencies: - vitest: 2.0.2 - - '@vitest/expect@2.0.2': - resolution: {integrity: sha512-nKAvxBYqcDugYZ4nJvnm5OR8eDJdgWjk4XM9owQKUjzW70q0icGV2HVnQOyYsp906xJaBDUXw0+9EHw2T8e0mQ==} + '@vitest/pretty-format@5.0.3': + resolution: {integrity: sha512-3hr5eV5z2ff0l2Q1vaKQbB4j3M3tElw8stDokEOTqXJB3Es5VrT8gBRIMY59gGuFp8Y61sWlWFK1aNUH5dHDYw==} - '@vitest/pretty-format@2.0.2': - resolution: {integrity: sha512-SBCyOXfGVvddRd9r2PwoVR0fonQjh9BMIcBMlSzbcNwFfGr6ZhOhvBzurjvi2F4ryut2HcqiFhNeDVGwru8tLg==} + '@vitest/spy@5.0.3': + resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} - '@vitest/runner@2.0.2': - resolution: {integrity: sha512-OCh437Vi8Wdbif1e0OvQcbfM3sW4s2lpmOjAE7qfLrpzJX2M7J1IQlNvEcb/fu6kaIB9n9n35wS0G2Q3en5kHg==} + '@vitest/ui@5.0.3': + resolution: {integrity: sha512-9dfvqDBvSbHM6y9YVXxAypkl2z7mrWozK/JFo237qI0cj/3M/kzDKiVZqzi3TE54Ler7Jditud7OMBZCyUvRHA==} + peerDependencies: + vitest: 5.0.3 - '@vitest/snapshot@2.0.2': - resolution: {integrity: sha512-Yc2ewhhZhx+0f9cSUdfzPRcsM6PhIb+S43wxE7OG0kTxqgqzo8tHkXFuFlndXeDMp09G3sY/X5OAo/RfYydf1g==} + '@vitest/utils@5.0.3': + resolution: {integrity: sha512-77fDTZv8eu1CzlIfgPU2yrl5MXSUaUAOvgdZEeVm3X3WIxUubIX2SforqS6176lT32HCF6uBVMYVqfHxw7o6eg==} - '@vitest/spy@2.0.2': - resolution: {integrity: sha512-MgwJ4AZtCgqyp2d7WcQVE8aNG5vQ9zu9qMPYQHjsld/QVsrvg78beNrXdO4HYkP0lDahCO3P4F27aagIag+SGQ==} + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true - '@vitest/utils@2.0.2': - resolution: {integrity: sha512-pxCY1v7kmOCWYWjzc0zfjGTA3Wmn8PKnlPvSrsA643P1NHl1fOyXj2Q9SaNlrlFE+ivCsxM80Ov3AR82RmHCWQ==} + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} - '@volar/language-core@2.3.4': - resolution: {integrity: sha512-wXBhY11qG6pCDAqDnbBRFIDSIwbqkWI7no+lj5+L7IlA7HRIjRP7YQLGzT0LF4lS6eHkMSsclXqy9DwYJasZTQ==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} - '@volar/language-core@2.4.0-alpha.18': - resolution: {integrity: sha512-JAYeJvYQQROmVRtSBIczaPjP3DX4QW1fOqW1Ebs0d3Y3EwSNRglz03dSv0Dm61dzd0Yx3WgTW3hndDnTQqgmyg==} + ast-v8-to-istanbul@1.0.7: + resolution: {integrity: sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==} - '@volar/source-map@2.3.4': - resolution: {integrity: sha512-C+t63nwcblqLIVTYXaVi/+gC8NukDaDIQI72J3R7aXGvtgaVB16c+J8Iz7/VfOy7kjYv7lf5GhBny6ACw9fTGQ==} + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - '@volar/source-map@2.4.0-alpha.18': - resolution: {integrity: sha512-MTeCV9MUwwsH0sNFiZwKtFrrVZUK6p8ioZs3xFzHc2cvDXHWlYN3bChdQtwKX+FY2HG6H3CfAu1pKijolzIQ8g==} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} - '@volar/typescript@2.3.4': - resolution: {integrity: sha512-acCvt7dZECyKcvO5geNybmrqOsu9u8n5XP1rfiYsOLYGPxvHRav9BVmEdRyZ3vvY6mNyQ1wLL5Hday4IShe17w==} + chai@6.3.0: + resolution: {integrity: sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==} + engines: {node: '>=18'} - '@volar/typescript@2.4.0-alpha.18': - resolution: {integrity: sha512-sXh5Y8sqGUkgxpMWUGvRXggxYHAVxg0Pa1C42lQZuPDrW6vHJPR0VCK8Sr7WJsAW530HuNQT/ZIskmXtxjybMQ==} + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} - '@vue/compiler-core@3.4.31': - resolution: {integrity: sha512-skOiodXWTV3DxfDhB4rOf3OGalpITLlgCeOwb+Y9GJpfQ8ErigdBUHomBzvG78JoVE8MJoQsb+qhZiHfKeNeEg==} + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - '@vue/compiler-dom@3.4.31': - resolution: {integrity: sha512-wK424WMXsG1IGMyDGyLqB+TbmEBFM78hIsOJ9QwUVLGrcSk0ak6zYty7Pj8ftm7nEtdU/DGQxAXp0/lM/2cEpQ==} + cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} - '@vue/compiler-vue2@2.7.16': - resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} - '@vue/language-core@2.0.29': - resolution: {integrity: sha512-o2qz9JPjhdoVj8D2+9bDXbaI4q2uZTHQA/dbyZT4Bj1FR9viZxDJnLcKVHfxdn6wsOzRgpqIzJEEmSSvgMvDTQ==} + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} peerDependencies: - typescript: 5.5.3 + supports-color: '*' peerDependenciesMeta: - typescript: + supports-color: optional: true - '@vue/shared@3.4.31': - resolution: {integrity: sha512-Yp3wtJk//8cO4NItOPpi3QkLExAr/aLBGZMmTtW9WpdwBCJpRM6zj9WgWktXAl8IDIozwNMByT45JP3tO3ACWA==} - - '@wdio/config@8.40.2': - resolution: {integrity: sha512-RED2vcdX5Zdd6r+K+aWcjK4douxjJY4LP/8YvvavgqM0TURd5PDI0Y7IEz7+BIJOT4Uh+3atZawIN9/3yWFeag==} - engines: {node: ^16.13 || >=18} + decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - '@wdio/logger@8.38.0': - resolution: {integrity: sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==} - engines: {node: ^16.13 || >=18} + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} - '@wdio/protocols@8.38.0': - resolution: {integrity: sha512-7BPi7aXwUtnXZPeWJRmnCNFjyDvGrXlBmN9D4Pi58nILkyjVRQKEY9/qv/pcdyB0cvmIvw++Kl/1Lg+RxG++UA==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} - '@wdio/repl@8.24.12': - resolution: {integrity: sha512-321F3sWafnlw93uRTSjEBVuvWCxTkWNDs7ektQS15drrroL3TMeFOynu4rDrIz0jXD9Vas0HCD2Tq/P0uxFLdw==} - engines: {node: ^16.13 || >=18} + electron@44.5.1: + resolution: {integrity: sha512-lx7AYoFIiyvEvgvpyWO9kOC60xtNPpiPZAth6Fmg9NutzpCAMLwpUlaBY2EXfZyIgUdzEmLMj9KvK0Vrokl1yQ==} + engines: {node: '>= 22.12.0'} + hasBin: true - '@wdio/types@8.39.0': - resolution: {integrity: sha512-86lcYROTapOJuFd9ouomFDfzDnv3Kn+jE0RmqfvN9frZAeLVJ5IKjX9M6HjplsyTZhjGO1uCaehmzx+HJus33Q==} - engines: {node: ^16.13 || >=18} + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} - '@wdio/utils@8.40.2': - resolution: {integrity: sha512-leYcCUSaAdLUCVKqRKNgMCASPOUo/VvOTKETiZ/qpdY2azCBt/KnLugtiycCzakeYg6Kp+VIjx5fkm0M7y4qhA==} - engines: {node: ^16.13 || >=18} + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - '@zip.js/zip.js@2.7.48': - resolution: {integrity: sha512-J7cliimZ2snAbr0IhLx2U8BwfA1pKucahKzTpFtYq4hEgKxwvFJcIjCIVNPwQpfVab7iVP+AKmoH1gidBlyhiQ==} - engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'} + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} + esbuild@0.23.0: + resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + engines: {node: '>=18'} + hasBin: true - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - acorn@7.4.1: - resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} - engines: {node: '>=0.4.0'} - hasBin: true + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} - acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} - agent-base@7.1.1: - resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} - engines: {node: '>= 14'} + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} - ajv-draft-04@1.0.0: - resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} - peerDependencies: - ajv: ^8.5.0 - peerDependenciesMeta: - ajv: - optional: true + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: - ajv: ^8.0.0 + picomatch: ^3 || ^4 peerDependenciesMeta: - ajv: + picomatch: optional: true - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - - ajv@8.13.0: - resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} - - ajv@8.16.0: - resolution: {integrity: sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==} + fflate@0.8.3: + resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} + flatted@3.4.4: + resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] - ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} + get-tsconfig@4.7.6: + resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} - ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} - archiver-utils@5.0.2: - resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} - archiver@7.0.1: - resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + https-proxy-agent@7.0.5: + resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} engines: {node: '>= 14'} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + human-id@4.2.1: + resolution: {integrity: sha512-zPGsiS+dWoTZtZ4AtpA9Y+BdSFSNWvnouNlWNoUFyAM6xHOHmdCvqO3k8AIbdamCOv4gUFUVNPf6rJFfc4UiJw==} + hasBin: true - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} - array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + jsdom@24.1.0: + resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true - array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + jsonc-parser@3.3.1: + resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} - array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + launch-editor@2.14.1: + resolution: {integrity: sha512-QWBrQsMpH7gPr965dsKD/3cKWiNoTjpATQf++Xq63N6sKRGMwlVXz41O1IZTMfZQgBctD/K5Zt06+/I6pP6+HA==} - array.prototype.toreversed@1.1.2: - resolution: {integrity: sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==} + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] - arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async@3.2.5: - resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axe-core@4.9.1: - resolution: {integrity: sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==} - engines: {node: '>=4'} + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] - axobject-query@3.1.1: - resolution: {integrity: sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==} + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] - b4a@1.6.6: - resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] - bare-events@2.4.2: - resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] - bare-fs@2.3.1: - resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] - bare-os@2.4.0: - resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] - bare-path@2.1.3: - resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] - bare-stream@2.1.3: - resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} - basic-ftp@5.0.5: - resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} - engines: {node: '>=10.0.0'} + magic-string@1.4.2: + resolution: {integrity: sha512-vG+rjFRj1PqdIBozIxAGMjPlOhaVe+GXpbttY/iSK7rGcJRMlwNJO7dcUwmUqkymsFLJiNGI06t4D7Fr7yRC9g==} - better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + magicast@0.5.5: + resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} - boolean@3.2.0: - resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + mrmime@2.0.0: + resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + engines: {node: '>=10'} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - browserslist@4.23.2: - resolution: {integrity: sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + nanoid@3.3.19: + resolution: {integrity: sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-crc32@1.0.0: - resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} - engines: {node: '>=8.0.0'} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} - - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - - cacheable-lookup@7.0.0: - resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} - engines: {node: '>=14.16'} - - cacheable-request@10.2.14: - resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} - engines: {node: '>=14.16'} - - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} + nwsapi@2.2.12: + resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} - caller-callsite@2.0.0: - resolution: {integrity: sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==} - engines: {node: '>=4'} + oxlint@1.86.0: + resolution: {integrity: sha512-og0lhgvZfgGF//gOOmZXvtr+GmBbAGEnbEhv/QUg7UW2Wi4wHMnJbnMD+zuHgPdJxdklfgpPupdlAaAySyxrZg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2003' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true - caller-path@2.0.0: - resolution: {integrity: sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==} - engines: {node: '>=4'} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} - callsites@2.0.0: - resolution: {integrity: sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==} - engines: {node: '>=4'} + parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - caniuse-lite@1.0.30001642: - resolution: {integrity: sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - chai@5.1.1: - resolution: {integrity: sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==} + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} - chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + playwright-core@1.63.0: + resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} + engines: {node: '>=20'} + hasBin: true - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + playwright@1.63.0: + resolution: {integrity: sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==} + engines: {node: '>=20'} + hasBin: true - check-error@2.1.1: - resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} - engines: {node: '>= 16'} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} - chromium-bidi@0.5.8: - resolution: {integrity: sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==} - peerDependencies: - devtools-protocol: '*' + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} - ci-info@2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} + psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - - color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - commander@9.5.0: - resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} - engines: {node: ^12.20.0 || >=14} + rolldown@1.2.12: + resolution: {integrity: sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true - compare-versions@6.1.1: - resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} - compress-commons@6.0.2: - resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} - engines: {node: '>= 14'} + rrweb-cssom@0.7.1: + resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} - computeds@0.0.1: - resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} - confbox@0.1.7: - resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true - confusing-browser-globals@1.0.11: - resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + shell-quote@1.11.0: + resolution: {integrity: sha512-JdxDPD0DBTyu08pq0kPC0xSNet/qsU07qT6IsX1AS8oO2ICNRY4ldNa8OAI6PuwAH8tG3lxEhbqmyp4Dw4036g==} + engines: {node: '>= 0.4'} - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + source-map-js@1.2.2: + resolution: {integrity: sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==} + engines: {node: '>=0.10.0'} - cosmiconfig@5.2.1: - resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==} - engines: {node: '>=4'} + std-env@4.3.0: + resolution: {integrity: sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==} - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true + sumchecker@3.0.1: + resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} + engines: {node: '>= 8.0'} - crc32-stream@6.0.0: - resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} - engines: {node: '>= 14'} + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} - cross-env@5.2.1: - resolution: {integrity: sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==} - engines: {node: '>=4.0'} - hasBin: true + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} + tinybench@6.2.0: + resolution: {integrity: sha512-78U2TlB2CnVenajOFzf3BKSm0J6oz5L0NV7g32LCPccvYc0lbWvys4d3uUUCS2B1N8PAf2+aekR8i1KbC3HO7Q==} + engines: {node: '>=20.0.0'} - cross-spawn@6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} - engines: {node: '>=4.8'} + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} - cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} + tinyrainbow@3.2.0: + resolution: {integrity: sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==} + engines: {node: '>=14.0.0'} - css-shorthand-properties@1.1.1: - resolution: {integrity: sha512-Md+Juc7M3uOdbAFwOYlTrccIZ7oCFuzrhKYQjdeUEW/sE1hv17Jp/Bws+ReOPpGVBTYCBoYo+G17V5Qo8QQ75A==} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} - css-value@0.0.1: - resolution: {integrity: sha512-FUV3xaJ63buRLgHrLQVlVgQnQdR4yqdLGaDu7g8CQcWjInDfM9plBTPI9FRfpahju1UBSaMckeb2/46ApS/V1Q==} + tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} - cssstyle@4.0.1: - resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} engines: {node: '>=18'} - damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} + tsx@4.17.0: + resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==} + engines: {node: '>=18.0.0'} + hasBin: true - data-urls@5.0.0: - resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} - engines: {node: '>=18'} + typescript@7.0.2: + resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} + engines: {node: '>=16.20.0'} + hasBin: true - data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + undici-types@7.24.6: + resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} - data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + undici@7.30.0: + resolution: {integrity: sha512-dkrQXeHSaoamnItlYbmzG0wFYrM0ZwDxCIg0A7aKjTyyhh9svRzCNFEzV+Vm05/yehjCzjDZ31KXfGEjYSztDQ==} + engines: {node: '>=20.18.1'} - data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} - de-indent@1.0.2: - resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + vite@8.3.2: + resolution: {integrity: sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true peerDependencies: - supports-color: '*' + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.7.1 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: - supports-color: + '@types/node': optional: true - - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: + '@vitejs/devtools': optional: true - - debug@4.3.5: - resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: optional: true - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} - engines: {node: '>=6.0'} + vitest@5.0.3: + resolution: {integrity: sha512-xMw97S3rjdtj5dkVat7jCsqWBpvchs3RlpQctUqwJD0KkERk40vz2fJ77lDwW/Vzh/pk18eItYAzkodhSes3jQ==} + engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0} + hasBin: true peerDependencies: - supports-color: '*' + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 5.0.3 + '@vitest/browser-preview': 5.0.3 + '@vitest/browser-webdriverio': ^5.0.0-beta.5 || >=5.0.0 + '@vitest/coverage-istanbul': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + '@vitest/ui': 5.0.3 + happy-dom: '*' + jsdom: '*' + vite: ^6.4.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: - supports-color: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: optional: true - decamelize@6.0.0: - resolution: {integrity: sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - decimal.js@10.4.3: - resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - deep-eql@5.0.2: - resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} - engines: {node: '>=6'} - - deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - deepmerge-ts@5.1.0: - resolution: {integrity: sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==} - engines: {node: '>=16.0.0'} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} - detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - - devtools-protocol@0.0.1232444: - resolution: {integrity: sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==} - - devtools-protocol@0.0.1335233: - resolution: {integrity: sha512-bNTJw/m+v0JvQEsaI0l+i6mETHHf7VwZbQzT5GNSveGuYjip8uyjeF/qg84bsIPU+lFypnZr10a+cbcee6I8pg==} - - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} - - dom-accessibility-api@0.5.16: - resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - edge-paths@3.0.5: - resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} - engines: {node: '>=14.0.0'} - - edgedriver@5.6.1: - resolution: {integrity: sha512-3Ve9cd5ziLByUdigw6zovVeWJjVs8QHVmqOB0sJ0WNeVPcwf4p18GnxMmVvlFmYRloUwf5suNuorea4QzwBIOA==} - hasBin: true - - electron-to-chromium@1.4.827: - resolution: {integrity: sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==} - - electron@31.3.1: - resolution: {integrity: sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==} - engines: {node: '>= 12.20.55'} - hasBin: true - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - - enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} - - entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - - es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} - - es6-error@4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.23.0: - resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} - engines: {node: '>=18'} - hasBin: true - - escalade@3.1.2: - resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} - engines: {node: '>=6'} - - escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - - eslint-config-airbnb-base@14.2.1: - resolution: {integrity: sha512-GOrQyDtVEc1Xy20U7vsB2yAoB4nBlfH5HZJeatRXHleO+OS5Ot+MWij4Dpltw4/DyIkqUfqz1epfhVR5XWWQPA==} - engines: {node: '>= 6'} - peerDependencies: - eslint: ^5.16.0 || ^6.8.0 || ^7.2.0 - eslint-plugin-import: ^2.22.1 - - eslint-config-airbnb-typescript@12.3.1: - resolution: {integrity: sha512-ql/Pe6/hppYuRp4m3iPaHJqkBB7dgeEmGPQ6X0UNmrQOfTF+dXw29/ZjU2kQ6RDoLxaxOA+Xqv07Vbef6oVTWw==} - - eslint-config-airbnb@18.2.1: - resolution: {integrity: sha512-glZNDEZ36VdlZWoxn/bUR1r/sdFKPd1mHPbqUtkctgNG4yT2DLLtJ3D+yCV+jzZCc2V1nBVkmdknOJBZ5Hc0fg==} - engines: {node: '>= 6'} - peerDependencies: - eslint: ^5.16.0 || ^6.8.0 || ^7.2.0 - eslint-plugin-import: ^2.22.1 - eslint-plugin-jsx-a11y: ^6.4.1 - eslint-plugin-react: ^7.21.5 - eslint-plugin-react-hooks: ^4 || ^3 || ^2.3.0 || ^1.7.0 - - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-module-utils@2.8.1: - resolution: {integrity: sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.29.1: - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-jsx-a11y@6.9.0: - resolution: {integrity: sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - - eslint-plugin-react-hooks@4.6.2: - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - - eslint-plugin-react@7.34.3: - resolution: {integrity: sha512-aoW4MV891jkUulwDApQbPYTVZmeuSyFrudpbTAQuj5Fv8VL+o6df2xIGpw8B0hPjAaih1/Fb0om9grCdyFYemA==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 - - eslint-scope@5.1.1: - resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} - engines: {node: '>=8.0.0'} - - eslint-utils@2.1.0: - resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} - engines: {node: '>=6'} - - eslint-utils@3.0.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - - eslint-visitor-keys@1.3.0: - resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} - engines: {node: '>=4'} - - eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - - eslint@7.32.0: - resolution: {integrity: sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==} - engines: {node: ^10.12.0 || >=12.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true - - espree@7.3.1: - resolution: {integrity: sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==} - engines: {node: ^10.12.0 || >=12.0.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@4.3.0: - resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - execa@1.0.0: - resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==} - engines: {node: '>=6'} - - execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} - - extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - - fast-deep-equal@2.0.1: - resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-fifo@1.3.2: - resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} - - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-xml-parser@4.4.1: - resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} - hasBin: true - - fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - find-up@3.0.0: - resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} - engines: {node: '>=6'} - - find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - - flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} - - flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - - foreground-child@3.2.1: - resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} - engines: {node: '>=14'} - - form-data-encoder@2.1.4: - resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} - engines: {node: '>= 14.17'} - - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - - fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} - - functional-red-black-tree@1.0.1: - resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - geckodriver@4.4.2: - resolution: {integrity: sha512-/JFJ7DJPJUvDhLjzQk+DwjlkAmiShddfRHhZ/xVL9FWbza5Bi3UMGmmerEKqD69JbRs7R81ZW31co686mdYZyA==} - engines: {node: ^16.13 || >=18 || >=20} - hasBin: true - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-func-name@2.0.2: - resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} - - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - - get-port@7.1.0: - resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} - engines: {node: '>=16'} - - get-stdin@6.0.0: - resolution: {integrity: sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==} - engines: {node: '>=4'} - - get-stream@4.1.0: - resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==} - engines: {node: '>=6'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - - get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.7.6: - resolution: {integrity: sha512-ZAqrLlu18NbDdRaHq+AKXzAmqIUPswPWKUchfytdAjiRFnCe5ojG2bstg6mRiZabkKfCoL/e98pbBELIV/YCeA==} - - get-uri@6.0.3: - resolution: {integrity: sha512-BzUrJBS9EcUb4cFol8r4W3v1cPsSyajLSthNkz5BxbpDcHN5tIrM10E2eNvfnvBn3DaT3DUgx0OpsBKkaOpanw==} - engines: {node: '>= 14'} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - global-agent@3.0.0: - resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} - engines: {node: '>=10.0'} - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - - gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} - - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - - got@12.6.1: - resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} - engines: {node: '>=14.16'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - grapheme-splitter@1.0.4: - resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} - - graphql@16.9.0: - resolution: {integrity: sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - - has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - - has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} - - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - - headers-polyfill@4.0.3: - resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} - - hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - - html-encoding-sniffer@4.0.0: - resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} - engines: {node: '>=18'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - http-cache-semantics@4.1.1: - resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - - http-proxy-agent@7.0.2: - resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} - engines: {node: '>= 14'} - - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - - http2-wrapper@2.2.1: - resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} - - human-id@4.1.1: - resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} - hasBin: true - - human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - - husky@1.3.1: - resolution: {integrity: sha512-86U6sVVVf4b5NYSZ0yvv88dRgBSSXXmHaiq5pP4KDj5JVzdwKgBjEtUPOm8hcoytezFwbU+7gotXNhpHdystlg==} - engines: {node: '>=6'} - hasBin: true - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@4.0.6: - resolution: {integrity: sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==} - engines: {node: '>= 4'} - - ignore@5.3.1: - resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} - engines: {node: '>= 4'} - - immediate@3.0.6: - resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - - import-fresh@2.0.0: - resolution: {integrity: sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==} - engines: {node: '>=4'} - - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - - import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - - import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} - - ip-address@9.0.5: - resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} - engines: {node: '>= 12'} - - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - - is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} - - is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} - - is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-ci@2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true - - is-core-module@2.14.0: - resolution: {integrity: sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} - - is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} - - is-directory@0.3.1: - resolution: {integrity: sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==} - engines: {node: '>=0.10.0'} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} - - is-node-process@1.2.0: - resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} - - is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-potential-custom-element-name@1.0.1: - resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - - is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} - - is-stream@1.1.0: - resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} - engines: {node: '>=0.10.0'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} - - is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} - - is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - - is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} - - is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isexe@3.1.1: - resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} - engines: {node: '>=16'} - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-instrument@6.0.3: - resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} - engines: {node: '>=10'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-lib-source-maps@5.0.6: - resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} - engines: {node: '>=10'} - - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} - engines: {node: '>=8'} - - iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jju@1.4.0: - resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true - - jsbn@1.1.0: - resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} - - jsdom@24.1.0: - resolution: {integrity: sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==} - engines: {node: '>=18'} - peerDependencies: - canvas: ^2.11.2 - peerDependenciesMeta: - canvas: - optional: true - - jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - jszip@3.10.1: - resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - - ky@0.33.3: - resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==} - engines: {node: '>=14.16'} - - language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - - language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} - - lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - lie@3.3.0: - resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} - - local-pkg@0.5.0: - resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} - engines: {node: '>=14'} - - locate-app@2.4.26: - resolution: {integrity: sha512-xpWY9gFzBgA9e7yP0Bwrr+T9lxUi/7jL9+op7++GAluh9zKHMYA3pY8gNsGcquw1H09GIyf6Ij105OsCLBzXZg==} - - locate-path@3.0.0: - resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} - engines: {node: '>=6'} - - locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - - lodash.clonedeep@4.5.0: - resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - - lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - - lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - - lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} - - loglevel-plugin-prefix@0.8.4: - resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} - - loglevel@1.9.1: - resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} - engines: {node: '>= 0.6.0'} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - loupe@3.1.1: - resolution: {integrity: sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==} - - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - lowercase-keys@3.0.0: - resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - lz-string@1.5.0: - resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} - hasBin: true - - magic-string@0.30.10: - resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} - - magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} - - magicast@0.3.4: - resolution: {integrity: sha512-TyDF/Pn36bBji9rWKHlZe+PZb6Mx5V8IHCSxk7X4aljM4e/vyDvZZYwHewdVaqiA0nb3ghfHU/6AUpDxWoER2Q==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - matcher@3.0.0: - resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} - engines: {node: '>=10'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - micromatch@4.0.7: - resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==} - engines: {node: '>=8.6'} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - mimic-response@4.0.0: - resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - minimatch@3.0.8: - resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mitt@3.0.1: - resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - - mkdirp-classic@0.5.3: - resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - - mlly@1.7.1: - resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} - engines: {node: '>=10'} - - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - msw@2.3.1: - resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - typescript: 5.5.3 - peerDependenciesMeta: - typescript: - optional: true - - muggle-string@0.4.1: - resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} - - mute-stream@1.0.0: - resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - netmask@2.0.2: - resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} - engines: {node: '>= 0.4.0'} - - nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - deprecated: Use your platform's native DOMException instead - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-releases@2.0.14: - resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - - normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - normalize-url@8.0.1: - resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} - engines: {node: '>=14.16'} - - npm-run-path@2.0.2: - resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} - engines: {node: '>=4'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - nwsapi@2.2.12: - resolution: {integrity: sha512-qXDmcVlZV4XRtKFzddidpfVP4oMSGhga+xdMc25mv8kaLUHtgzCDhUxkrN8exkGdTlLNaXj7CV3GtON7zuGZ+w==} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} - - object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} - - object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.hasown@1.1.4: - resolution: {integrity: sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==} - engines: {node: '>= 0.4'} - - object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - - outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - - outvariant@1.4.3: - resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} - - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - p-cancelable@3.0.0: - resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} - engines: {node: '>=12.20'} - - p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - - p-locate@3.0.0: - resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} - engines: {node: '>=6'} - - p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - - p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - - p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - - pac-proxy-agent@7.0.2: - resolution: {integrity: sha512-BFi3vZnO9X5Qt6NRz7ZOaPja3ic0PhlsmCRYLOpN11+mWBCR6XJDqW5RF3j8jm4WGGQZtBA+bTfxYzeKW73eHg==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - - package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - - package-manager-detector@0.2.11: - resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} - - pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} - - parse5@7.1.2: - resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@3.0.0: - resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} - engines: {node: '>=4'} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-to-regexp@6.2.2: - resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - pathe@1.1.2: - resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} - - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} - engines: {node: '>= 14.16'} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - picocolors@1.0.1: - resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - - pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} - - pkg-dir@3.0.0: - resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} - engines: {node: '>=6'} - - pkg-types@1.1.3: - resolution: {integrity: sha512-+JrgthZG6m3ckicaOB74TwQ+tBWsFl3qVQg7mN8ulwSOElJ7gBhKzj2VkCPnZ4NlF6kEquYU+RIYNVAvzd54UA==} - - playwright-core@1.45.1: - resolution: {integrity: sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.45.1: - resolution: {integrity: sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg==} - engines: {node: '>=18'} - hasBin: true - - please-upgrade-node@3.2.0: - resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - - postcss@8.4.39: - resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - - pretty-format@27.5.1: - resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} - engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - proxy-agent@6.3.1: - resolution: {integrity: sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - psl@1.9.0: - resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} - - pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - puppeteer-core@21.11.0: - resolution: {integrity: sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==} - engines: {node: '>=16.13.2'} - - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} - - query-selector-shadow-dom@1.0.1: - resolution: {integrity: sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==} - - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - queue-tick@1.0.1: - resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - - read-pkg@4.0.1: - resolution: {integrity: sha512-+UBirHHDm5J+3WDmLBZYSklRYg82nMlz+enn+GMZ22nSR2f4bzxmhso6rzQW/3mT2PVzpzDTiYIZahk8UmZ44w==} - engines: {node: '>=6'} - - read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@4.5.2: - resolution: {integrity: sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - - reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - - regexpp@3.2.0: - resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} - engines: {node: '>=8'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - resolve-from@3.0.0: - resolution: {integrity: sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==} - engines: {node: '>=4'} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true - - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - - responselike@3.0.0: - resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} - engines: {node: '>=14.16'} - - resq@1.11.0: - resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - rgb2hex@0.2.5: - resolution: {integrity: sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==} - - rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true - - roarr@2.15.4: - resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} - engines: {node: '>=8.0'} - - rollup@4.18.1: - resolution: {integrity: sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - - rrweb-cssom@0.6.0: - resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} - - rrweb-cssom@0.7.1: - resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} - - run-node@1.0.0: - resolution: {integrity: sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==} - engines: {node: '>=4'} - hasBin: true - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - safaridriver@0.1.2: - resolution: {integrity: sha512-4R309+gWflJktzPXBQCobbWEHlzC4aK3a+Ov3tz2Ib2aBxiwd11phkdIBH1l0EO22x24CJMUQkpKFumRriCSRg==} - - safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - saxes@6.0.0: - resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} - engines: {node: '>=v12.22.7'} - - semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - - semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true - - semver@7.6.2: - resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} - engines: {node: '>=10'} - hasBin: true - - serialize-error@11.0.3: - resolution: {integrity: sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==} - engines: {node: '>=14.16'} - - serialize-error@7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - - shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sirv@2.0.4: - resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} - engines: {node: '>= 10'} - - slash@2.0.0: - resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} - engines: {node: '>=6'} - - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - - slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks-proxy-agent@8.0.4: - resolution: {integrity: sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==} - engines: {node: '>= 14'} - - socks@2.8.3: - resolution: {integrity: sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - spacetrim@0.11.39: - resolution: {integrity: sha512-S/baW29azJ7py5ausQRE2S6uEDQnlxgMHOEEq4V770ooBDD1/9kZnxRcco/tjZYuDuqYXblCk/r3N13ZmvHZ2g==} - - spawndamnit@3.0.1: - resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} - - spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} - - spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} - - spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - - spdx-license-ids@3.0.18: - resolution: {integrity: sha512-xxRs31BqRYHwiMzudOrpSiHtZ8i/GeionCBDSilhYRj+9gIcI8wCZTlXZKu9vZIVqViP3dcp9qE5G6AlIaD+TQ==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - std-env@3.7.0: - resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} - - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - - streamx@2.18.0: - resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==} - - strict-event-emitter@0.5.1: - resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} - - string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string.prototype.includes@2.0.0: - resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} - - string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} - - string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-eof@1.0.0: - resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} - engines: {node: '>=0.10.0'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strnum@1.0.5: - resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==} - - sumchecker@3.0.1: - resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} - engines: {node: '>= 8.0'} - - supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - symbol-tree@3.2.4: - resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - - table@6.8.2: - resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} - engines: {node: '>=10.0.0'} - - tar-fs@3.0.4: - resolution: {integrity: sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==} - - tar-fs@3.0.6: - resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} - - tar-stream@3.1.7: - resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - - term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - - text-decoder@1.1.1: - resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} - - text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - - through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - - tinybench@2.8.0: - resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} - - tinypool@1.0.0: - resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} - engines: {node: ^18.0.0 || >=20.0.0} - - tinyrainbow@1.2.0: - resolution: {integrity: sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==} - engines: {node: '>=14.0.0'} - - tinyspy@3.0.0: - resolution: {integrity: sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==} - engines: {node: '>=14.0.0'} - - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - - to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - tr46@5.0.0: - resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} - engines: {node: '>=18'} - - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - - tslib@1.14.1: - resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} - - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - - tsutils@3.21.0: - resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} - engines: {node: '>= 6'} - peerDependencies: - typescript: 5.5.3 - - tsx@4.17.0: - resolution: {integrity: sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==} - engines: {node: '>=18.0.0'} - hasBin: true - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - - type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@2.13.0: - resolution: {integrity: sha512-lPfAm42MxE4/456+QyIaaVBAwgpJb6xZ8PRu09utnhPdWwcyj9vgy6Sq0Z5yNbJ21EdxB5dRU/Qg8bsyAMtlcw==} - engines: {node: '>=12.20'} - - type-fest@2.19.0: - resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} - engines: {node: '>=12.20'} - - type-fest@4.21.0: - resolution: {integrity: sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==} - engines: {node: '>=16'} - - typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} - - typescript@5.5.3: - resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} - engines: {node: '>=14.17'} - hasBin: true - - ufo@1.5.4: - resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} - - unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} - - unbzip2-stream@1.4.3: - resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - undici-types@6.18.2: - resolution: {integrity: sha512-5ruQbENj95yDYJNS3TvcaxPMshV7aizdv/hWYjGIKoANWKjhWNBsr2YEuYZKodQulB1b8l7ILOuDQep3afowQQ==} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - update-browserslist-db@1.1.0: - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - - urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} - - userhome@1.0.0: - resolution: {integrity: sha512-ayFKY3H+Pwfy4W98yPdtH1VqH4psDeyW8lYYFzfecR9d6hqLpqhecktvYR3SEEXt7vG0S1JEpciI3g94pMErig==} - engines: {node: '>= 0.8.0'} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - v8-compile-cache@2.4.0: - resolution: {integrity: sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==} - - validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - - vite-node@2.0.2: - resolution: {integrity: sha512-w4vkSz1Wo+NIQg8pjlEn0jQbcM/0D+xVaYjhw3cvarTanLLBh54oNiRbsT8PNK5GfuST0IlVXjsNRoNlqvY/fw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - - vite-plugin-dts@4.0.3: - resolution: {integrity: sha512-+xnTsaONwU2kV6zhRjtbRJSGN41uFR/whqmcb4k4fftLFDJElxthp0PP5Fq8gMeM9ytWMt1yk5gGgekLREWYQQ==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - typescript: 5.5.3 - vite: '*' - peerDependenciesMeta: - vite: - optional: true - - vite@5.3.3: - resolution: {integrity: sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest@2.0.2: - resolution: {integrity: sha512-WlpZ9neRIjNBIOQwBYfBSr0+of5ZCbxT2TVGKW4Lv0c8+srCFIiRdsP7U009t8mMn821HQ4XKgkx5dVWpyoyLw==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 2.0.2 - '@vitest/ui': 2.0.2 - happy-dom: '*' - jsdom: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@types/node': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - vscode-uri@3.0.8: - resolution: {integrity: sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==} - - vue-tsc@2.0.29: - resolution: {integrity: sha512-MHhsfyxO3mYShZCGYNziSbc63x7cQ5g9kvijV7dRe1TTXBRLxXyL0FnXWpUF1xII2mJ86mwYpYsUmMwkmerq7Q==} - hasBin: true - peerDependencies: - typescript: 5.5.3 - - w3c-xmlserializer@5.0.0: - resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} - engines: {node: '>=18'} - - wait-port@1.1.0: - resolution: {integrity: sha512-3e04qkoN3LxTMLakdqeWth8nih8usyg+sf1Bgdf9wwUkp05iuK1eSY/QpLvscT/+F/gA89+LpUmmgBtesbqI2Q==} - engines: {node: '>=10'} - hasBin: true - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - webdriver@8.40.2: - resolution: {integrity: sha512-GoRR94m3yL8tWC9Myf+xIBSdVK8fi1ilZgEZZaYT8+XIWewR02dvrC6rml+/2ZjXUQzeee0RFGDwk9IC7cyYrg==} - engines: {node: ^16.13 || >=18} - - webdriverio@8.40.2: - resolution: {integrity: sha512-6yuzUlE064qNuMy98Du1+8QHbXk0st8qTWF7MDZRgYK19FGoy+KhQbaUv1wlFJuFHM0PiAYuduTURL4ub6HvzQ==} - engines: {node: ^16.13 || >=18} - peerDependencies: - devtools: ^8.14.0 - peerDependenciesMeta: - devtools: - optional: true - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - webidl-conversions@7.0.0: - resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} - engines: {node: '>=12'} - - whatwg-encoding@3.1.1: - resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} - engines: {node: '>=18'} - - whatwg-mimetype@4.0.0: - resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} - engines: {node: '>=18'} - - whatwg-url@14.0.0: - resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} - engines: {node: '>=18'} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - - which-builtin-type@1.1.3: - resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} - - which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} - hasBin: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.18.0: - resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xml-name-validator@5.0.0: - resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} - engines: {node: '>=18'} - - xmlchars@2.2.0: - resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - - zip-stream@6.0.1: - resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} - engines: {node: '>= 14'} - -snapshots: - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - - '@babel/code-frame@7.12.11': - dependencies: - '@babel/highlight': 7.24.7 - - '@babel/code-frame@7.24.7': - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.0.1 - - '@babel/compat-data@7.24.8': {} - - '@babel/core@7.24.8': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.8 - '@babel/helper-compilation-targets': 7.24.8 - '@babel/helper-module-transforms': 7.24.8(@babel/core@7.24.8) - '@babel/helpers': 7.24.8 - '@babel/parser': 7.24.8 - '@babel/template': 7.24.7 - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.8 - convert-source-map: 2.0.0 - debug: 4.3.5 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.24.8': - dependencies: - '@babel/types': 7.24.8 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - - '@babel/helper-compilation-targets@7.24.8': - dependencies: - '@babel/compat-data': 7.24.8 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.2 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-environment-visitor@7.24.7': - dependencies: - '@babel/types': 7.24.8 - - '@babel/helper-function-name@7.24.7': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.8 - - '@babel/helper-hoist-variables@7.24.7': - dependencies: - '@babel/types': 7.24.8 - - '@babel/helper-module-imports@7.24.7': - dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.24.8(@babel/core@7.24.8)': - dependencies: - '@babel/core': 7.24.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - transitivePeerDependencies: - - supports-color - - '@babel/helper-simple-access@7.24.7': - dependencies: - '@babel/traverse': 7.24.8 - '@babel/types': 7.24.8 - transitivePeerDependencies: - - supports-color - - '@babel/helper-split-export-declaration@7.24.7': - dependencies: - '@babel/types': 7.24.8 - - '@babel/helper-string-parser@7.24.8': {} - - '@babel/helper-validator-identifier@7.24.7': {} - - '@babel/helper-validator-option@7.24.8': {} - - '@babel/helpers@7.24.8': - dependencies: - '@babel/template': 7.24.7 - '@babel/types': 7.24.8 - - '@babel/highlight@7.24.7': - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.0.1 - - '@babel/parser@7.24.8': - dependencies: - '@babel/types': 7.24.8 - - '@babel/runtime@7.24.8': - dependencies: - regenerator-runtime: 0.14.1 - - '@babel/template@7.24.7': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.24.8 - '@babel/types': 7.24.8 - - '@babel/traverse@7.24.8': - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.24.8 - '@babel/helper-environment-visitor': 7.24.7 - '@babel/helper-function-name': 7.24.7 - '@babel/helper-hoist-variables': 7.24.7 - '@babel/helper-split-export-declaration': 7.24.7 - '@babel/parser': 7.24.8 - '@babel/types': 7.24.8 - debug: 4.3.5 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.24.8': - dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 - - '@bundled-es-modules/cookie@2.0.0': - dependencies: - cookie: 0.5.0 - - '@bundled-es-modules/statuses@1.0.1': - dependencies: - statuses: 2.0.1 - - '@changesets/apply-release-plan@7.0.12': - dependencies: - '@changesets/config': 3.1.1 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.4 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.6.2 - - '@changesets/assemble-release-plan@6.0.6': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.6.2 - - '@changesets/changelog-git@0.2.1': - dependencies: - '@changesets/types': 6.1.0 - - '@changesets/cli@2.29.2': - dependencies: - '@changesets/apply-release-plan': 7.0.12 - '@changesets/assemble-release-plan': 6.0.6 - '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.1 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.10 - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/should-skip-package': 0.1.2 - '@changesets/types': 6.1.0 - '@changesets/write': 0.4.0 - '@manypkg/get-packages': 1.1.3 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - mri: 1.2.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.11 - picocolors: 1.1.1 - resolve-from: 5.0.0 - semver: 7.6.2 - spawndamnit: 3.0.1 - term-size: 2.2.1 - - '@changesets/config@3.1.1': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.3 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - - '@changesets/errors@0.2.0': - dependencies: - extendable-error: 0.1.7 - - '@changesets/get-dependents-graph@2.1.3': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.1 - semver: 7.6.2 - - '@changesets/get-release-plan@4.0.10': - dependencies: - '@changesets/assemble-release-plan': 6.0.6 - '@changesets/config': 3.1.1 - '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.5 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/get-version-range-type@0.4.0': {} - - '@changesets/git@3.0.4': - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 3.0.1 - - '@changesets/logger@0.1.1': - dependencies: - picocolors: 1.1.1 - - '@changesets/parse@0.4.1': - dependencies: - '@changesets/types': 6.1.0 - js-yaml: 3.14.1 - - '@changesets/pre@2.0.2': - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - - '@changesets/read@0.6.5': - dependencies: - '@changesets/git': 3.0.4 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.1 - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.1 - - '@changesets/should-skip-package@0.1.2': - dependencies: - '@changesets/types': 6.1.0 - '@manypkg/get-packages': 1.1.3 - - '@changesets/types@4.1.0': {} - - '@changesets/types@6.1.0': {} - - '@changesets/write@0.4.0': - dependencies: - '@changesets/types': 6.1.0 - fs-extra: 7.0.1 - human-id: 4.1.1 - prettier: 2.8.8 - - '@electron/get@2.0.3': - dependencies: - debug: 4.3.5 - env-paths: 2.2.1 - fs-extra: 8.1.0 - got: 11.8.6 - progress: 2.0.3 - semver: 6.3.1 - sumchecker: 3.0.1 - optionalDependencies: - global-agent: 3.0.0 - transitivePeerDependencies: - - supports-color - - '@esbuild/aix-ppc64@0.21.5': - optional: true - - '@esbuild/aix-ppc64@0.23.0': - optional: true - - '@esbuild/android-arm64@0.21.5': - optional: true - - '@esbuild/android-arm64@0.23.0': - optional: true - - '@esbuild/android-arm@0.21.5': - optional: true - - '@esbuild/android-arm@0.23.0': - optional: true - - '@esbuild/android-x64@0.21.5': - optional: true - - '@esbuild/android-x64@0.23.0': - optional: true - - '@esbuild/darwin-arm64@0.21.5': - optional: true - - '@esbuild/darwin-arm64@0.23.0': - optional: true - - '@esbuild/darwin-x64@0.21.5': - optional: true - - '@esbuild/darwin-x64@0.23.0': - optional: true - - '@esbuild/freebsd-arm64@0.21.5': - optional: true - - '@esbuild/freebsd-arm64@0.23.0': - optional: true - - '@esbuild/freebsd-x64@0.21.5': - optional: true - - '@esbuild/freebsd-x64@0.23.0': - optional: true - - '@esbuild/linux-arm64@0.21.5': - optional: true - - '@esbuild/linux-arm64@0.23.0': - optional: true - - '@esbuild/linux-arm@0.21.5': - optional: true - - '@esbuild/linux-arm@0.23.0': - optional: true - - '@esbuild/linux-ia32@0.21.5': - optional: true - - '@esbuild/linux-ia32@0.23.0': - optional: true - - '@esbuild/linux-loong64@0.21.5': - optional: true - - '@esbuild/linux-loong64@0.23.0': - optional: true - - '@esbuild/linux-mips64el@0.21.5': - optional: true - - '@esbuild/linux-mips64el@0.23.0': - optional: true - - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.23.0': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.23.0': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.23.0': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/linux-x64@0.23.0': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.23.0': - optional: true - - '@esbuild/openbsd-arm64@0.23.0': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.23.0': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.23.0': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.23.0': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.23.0': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.23.0': - optional: true - - '@eslint/eslintrc@0.4.3': - dependencies: - ajv: 6.12.6 - debug: 4.3.5 - espree: 7.3.1 - globals: 13.24.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - js-yaml: 3.14.1 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/config-array@0.5.0': - dependencies: - '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.5 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@humanwhocodes/object-schema@1.2.1': {} - - '@inquirer/confirm@3.1.14': - dependencies: - '@inquirer/core': 9.0.2 - '@inquirer/type': 1.4.0 - - '@inquirer/core@9.0.2': - dependencies: - '@inquirer/figures': 1.0.3 - '@inquirer/type': 1.4.0 - '@types/mute-stream': 0.0.4 - '@types/node': 20.14.10 - '@types/wrap-ansi': 3.0.0 - ansi-escapes: 4.3.2 - cli-spinners: 2.9.2 - cli-width: 4.1.0 - mute-stream: 1.0.0 - signal-exit: 4.1.0 - strip-ansi: 6.0.1 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 - - '@inquirer/figures@1.0.3': {} - - '@inquirer/type@1.4.0': - dependencies: - mute-stream: 1.0.0 - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@istanbuljs/schema@0.1.3': {} - - '@jridgewell/gen-mapping@0.3.5': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@manypkg/find-root@1.1.0': - dependencies: - '@babel/runtime': 7.24.8 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - - '@manypkg/get-packages@1.1.3': - dependencies: - '@babel/runtime': 7.24.8 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - - '@microsoft/api-extractor-model@7.29.4(@types/node@22.3.0)': - dependencies: - '@microsoft/tsdoc': 0.15.0 - '@microsoft/tsdoc-config': 0.17.0 - '@rushstack/node-core-library': 5.5.1(@types/node@22.3.0) - transitivePeerDependencies: - - '@types/node' - - '@microsoft/api-extractor@7.47.4(@types/node@22.3.0)': - dependencies: - '@microsoft/api-extractor-model': 7.29.4(@types/node@22.3.0) - '@microsoft/tsdoc': 0.15.0 - '@microsoft/tsdoc-config': 0.17.0 - '@rushstack/node-core-library': 5.5.1(@types/node@22.3.0) - '@rushstack/rig-package': 0.5.3 - '@rushstack/terminal': 0.13.3(@types/node@22.3.0) - '@rushstack/ts-command-line': 4.22.3(@types/node@22.3.0) - lodash: 4.17.21 - minimatch: 3.0.8 - resolve: 1.22.8 - semver: 7.5.4 - source-map: 0.6.1 - typescript: 5.5.3 - transitivePeerDependencies: - - '@types/node' - - '@microsoft/tsdoc-config@0.17.0': - dependencies: - '@microsoft/tsdoc': 0.15.0 - ajv: 8.12.0 - jju: 1.4.0 - resolve: 1.22.8 - - '@microsoft/tsdoc@0.15.0': {} - - '@mswjs/cookies@1.1.1': {} - - '@mswjs/interceptors@0.29.1': - dependencies: - '@open-draft/deferred-promise': 2.2.0 - '@open-draft/logger': 0.3.0 - '@open-draft/until': 2.1.0 - is-node-process: 1.2.0 - outvariant: 1.4.3 - strict-event-emitter: 0.5.1 - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - - '@open-draft/deferred-promise@2.2.0': {} - - '@open-draft/logger@0.3.0': - dependencies: - is-node-process: 1.2.0 - outvariant: 1.4.3 - - '@open-draft/until@2.1.0': {} - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@polka/url@1.0.0-next.25': {} - - '@promptbook/utils@0.63.0': - dependencies: - spacetrim: 0.11.39 - optional: true - - '@puppeteer/browsers@1.9.1': - dependencies: - debug: 4.3.4 - extract-zip: 2.0.1 - progress: 2.0.3 - proxy-agent: 6.3.1 - tar-fs: 3.0.4 - unbzip2-stream: 1.4.3 - yargs: 17.7.2 - transitivePeerDependencies: - - supports-color - optional: true - - '@rollup/pluginutils@5.1.0(rollup@4.18.1)': - dependencies: - '@types/estree': 1.0.5 - estree-walker: 2.0.2 - picomatch: 2.3.1 - optionalDependencies: - rollup: 4.18.1 - - '@rollup/rollup-android-arm-eabi@4.18.1': - optional: true - - '@rollup/rollup-android-arm64@4.18.1': - optional: true - - '@rollup/rollup-darwin-arm64@4.18.1': - optional: true - - '@rollup/rollup-darwin-x64@4.18.1': - optional: true - - '@rollup/rollup-linux-arm-gnueabihf@4.18.1': - optional: true - - '@rollup/rollup-linux-arm-musleabihf@4.18.1': - optional: true - - '@rollup/rollup-linux-arm64-gnu@4.18.1': - optional: true - - '@rollup/rollup-linux-arm64-musl@4.18.1': - optional: true - - '@rollup/rollup-linux-powerpc64le-gnu@4.18.1': - optional: true - - '@rollup/rollup-linux-riscv64-gnu@4.18.1': - optional: true - - '@rollup/rollup-linux-s390x-gnu@4.18.1': - optional: true - - '@rollup/rollup-linux-x64-gnu@4.18.1': - optional: true - - '@rollup/rollup-linux-x64-musl@4.18.1': - optional: true - - '@rollup/rollup-win32-arm64-msvc@4.18.1': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.18.1': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.18.1': - optional: true - - '@rushstack/node-core-library@5.5.1(@types/node@22.3.0)': - dependencies: - ajv: 8.13.0 - ajv-draft-04: 1.0.0(ajv@8.13.0) - ajv-formats: 3.0.1(ajv@8.13.0) - fs-extra: 7.0.1 - import-lazy: 4.0.0 - jju: 1.4.0 - resolve: 1.22.8 - semver: 7.5.4 - optionalDependencies: - '@types/node': 22.3.0 - - '@rushstack/rig-package@0.5.3': - dependencies: - resolve: 1.22.8 - strip-json-comments: 3.1.1 - - '@rushstack/terminal@0.13.3(@types/node@22.3.0)': - dependencies: - '@rushstack/node-core-library': 5.5.1(@types/node@22.3.0) - supports-color: 8.1.1 - optionalDependencies: - '@types/node': 22.3.0 - - '@rushstack/ts-command-line@4.22.3(@types/node@22.3.0)': - dependencies: - '@rushstack/terminal': 0.13.3(@types/node@22.3.0) - '@types/argparse': 1.0.38 - argparse: 1.0.10 - string-argv: 0.3.2 - transitivePeerDependencies: - - '@types/node' - - '@sindresorhus/is@4.6.0': {} - - '@sindresorhus/is@5.6.0': - optional: true - - '@szmarczak/http-timer@4.0.6': - dependencies: - defer-to-connect: 2.0.1 - - '@szmarczak/http-timer@5.0.1': - dependencies: - defer-to-connect: 2.0.1 - optional: true - - '@testing-library/dom@10.3.1': - dependencies: - '@babel/code-frame': 7.12.11 - '@babel/runtime': 7.24.8 - '@types/aria-query': 5.0.4 - aria-query: 5.3.0 - chalk: 4.1.2 - dom-accessibility-api: 0.5.16 - lz-string: 1.5.0 - pretty-format: 27.5.1 - - '@testing-library/user-event@14.5.2(@testing-library/dom@10.3.1)': - dependencies: - '@testing-library/dom': 10.3.1 - - '@tootallnate/quickjs-emscripten@0.23.0': - optional: true - - '@types/argparse@1.0.38': {} - - '@types/aria-query@5.0.4': {} - - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.0.4 - '@types/keyv': 3.1.4 - '@types/node': 22.3.0 - '@types/responselike': 1.0.3 - - '@types/cookie@0.6.0': {} - - '@types/estree@1.0.5': {} - - '@types/http-cache-semantics@4.0.4': {} - - '@types/json-schema@7.0.15': {} - - '@types/json5@0.0.29': {} - - '@types/keyv@3.1.4': - dependencies: - '@types/node': 22.3.0 - - '@types/mute-stream@0.0.4': - dependencies: - '@types/node': 20.14.10 - - '@types/node@12.20.55': {} - - '@types/node@20.14.10': - dependencies: - undici-types: 5.26.5 - - '@types/node@22.3.0': - dependencies: - undici-types: 6.18.2 - - '@types/responselike@1.0.3': - dependencies: - '@types/node': 22.3.0 - - '@types/statuses@2.0.5': {} - - '@types/which@2.0.2': - optional: true - - '@types/wrap-ansi@3.0.0': {} - - '@types/ws@8.5.12': - dependencies: - '@types/node': 22.3.0 - optional: true - - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 22.3.0 - optional: true - - '@typescript-eslint/eslint-plugin@4.33.0(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0)(typescript@5.5.3)': - dependencies: - '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.5.3) - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.5.3) - '@typescript-eslint/scope-manager': 4.33.0 - debug: 4.3.5 - eslint: 7.32.0 - functional-red-black-tree: 1.0.1 - ignore: 5.3.1 - regexpp: 3.2.0 - semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.3) - optionalDependencies: - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/experimental-utils@4.33.0(eslint@7.32.0)(typescript@5.5.3)': - dependencies: - '@types/json-schema': 7.0.15 - '@typescript-eslint/scope-manager': 4.33.0 - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.5.3) - eslint: 7.32.0 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@7.32.0) - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3)': - dependencies: - '@typescript-eslint/scope-manager': 4.33.0 - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/typescript-estree': 4.33.0(typescript@5.5.3) - debug: 4.3.5 - eslint: 7.32.0 - optionalDependencies: - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@4.33.0': - dependencies: - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/visitor-keys': 4.33.0 - - '@typescript-eslint/types@4.33.0': {} - - '@typescript-eslint/typescript-estree@4.33.0(typescript@5.5.3)': - dependencies: - '@typescript-eslint/types': 4.33.0 - '@typescript-eslint/visitor-keys': 4.33.0 - debug: 4.3.5 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.6.2 - tsutils: 3.21.0(typescript@5.5.3) - optionalDependencies: - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@4.33.0': - dependencies: - '@typescript-eslint/types': 4.33.0 - eslint-visitor-keys: 2.1.0 - - '@vitest/browser@2.0.2(playwright@1.45.1)(typescript@5.5.3)(vitest@2.0.2)(webdriverio@8.40.2)': - dependencies: - '@testing-library/dom': 10.3.1 - '@testing-library/user-event': 14.5.2(@testing-library/dom@10.3.1) - '@vitest/utils': 2.0.2 - magic-string: 0.30.10 - msw: 2.3.1(typescript@5.5.3) - sirv: 2.0.4 - vitest: 2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0) - ws: 8.18.0 - optionalDependencies: - playwright: 1.45.1 - webdriverio: 8.40.2 - transitivePeerDependencies: - - bufferutil - - typescript - - utf-8-validate - - '@vitest/coverage-istanbul@2.0.2(vitest@2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0))': - dependencies: - '@istanbuljs/schema': 0.1.3 - debug: 4.3.5 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-instrument: 6.0.3 - istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 5.0.6 - istanbul-reports: 3.1.7 - magicast: 0.3.4 - test-exclude: 7.0.1 - tinyrainbow: 1.2.0 - vitest: 2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0) - transitivePeerDependencies: - - supports-color - - '@vitest/expect@2.0.2': - dependencies: - '@vitest/spy': 2.0.2 - '@vitest/utils': 2.0.2 - chai: 5.1.1 - tinyrainbow: 1.2.0 - - '@vitest/pretty-format@2.0.2': - dependencies: - tinyrainbow: 1.2.0 - - '@vitest/runner@2.0.2': - dependencies: - '@vitest/utils': 2.0.2 - pathe: 1.1.2 - - '@vitest/snapshot@2.0.2': - dependencies: - '@vitest/pretty-format': 2.0.2 - magic-string: 0.30.10 - pathe: 1.1.2 - - '@vitest/spy@2.0.2': - dependencies: - tinyspy: 3.0.0 - - '@vitest/utils@2.0.2': - dependencies: - '@vitest/pretty-format': 2.0.2 - estree-walker: 3.0.3 - loupe: 3.1.1 - tinyrainbow: 1.2.0 - - '@volar/language-core@2.3.4': - dependencies: - '@volar/source-map': 2.3.4 - - '@volar/language-core@2.4.0-alpha.18': - dependencies: - '@volar/source-map': 2.4.0-alpha.18 - - '@volar/source-map@2.3.4': {} - - '@volar/source-map@2.4.0-alpha.18': {} - - '@volar/typescript@2.3.4': - dependencies: - '@volar/language-core': 2.3.4 - path-browserify: 1.0.1 - vscode-uri: 3.0.8 - - '@volar/typescript@2.4.0-alpha.18': - dependencies: - '@volar/language-core': 2.4.0-alpha.18 - path-browserify: 1.0.1 - vscode-uri: 3.0.8 - - '@vue/compiler-core@3.4.31': - dependencies: - '@babel/parser': 7.24.8 - '@vue/shared': 3.4.31 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.0 - - '@vue/compiler-dom@3.4.31': - dependencies: - '@vue/compiler-core': 3.4.31 - '@vue/shared': 3.4.31 - - '@vue/compiler-vue2@2.7.16': - dependencies: - de-indent: 1.0.2 - he: 1.2.0 - - '@vue/language-core@2.0.29(typescript@5.5.3)': - dependencies: - '@volar/language-core': 2.4.0-alpha.18 - '@vue/compiler-dom': 3.4.31 - '@vue/compiler-vue2': 2.7.16 - '@vue/shared': 3.4.31 - computeds: 0.0.1 - minimatch: 9.0.5 - muggle-string: 0.4.1 - path-browserify: 1.0.1 - optionalDependencies: - typescript: 5.5.3 - - '@vue/shared@3.4.31': {} - - '@wdio/config@8.40.2': - dependencies: - '@wdio/logger': 8.38.0 - '@wdio/types': 8.39.0 - '@wdio/utils': 8.40.2 - decamelize: 6.0.0 - deepmerge-ts: 5.1.0 - glob: 10.4.5 - import-meta-resolve: 4.1.0 - transitivePeerDependencies: - - supports-color - optional: true - - '@wdio/logger@8.38.0': - dependencies: - chalk: 5.3.0 - loglevel: 1.9.1 - loglevel-plugin-prefix: 0.8.4 - strip-ansi: 7.1.0 - optional: true - - '@wdio/protocols@8.38.0': - optional: true - - '@wdio/repl@8.24.12': - dependencies: - '@types/node': 20.14.10 - optional: true - - '@wdio/types@8.39.0': - dependencies: - '@types/node': 20.14.10 - optional: true - - '@wdio/utils@8.40.2': - dependencies: - '@puppeteer/browsers': 1.9.1 - '@wdio/logger': 8.38.0 - '@wdio/types': 8.39.0 - decamelize: 6.0.0 - deepmerge-ts: 5.1.0 - edgedriver: 5.6.1 - geckodriver: 4.4.2 - get-port: 7.1.0 - import-meta-resolve: 4.1.0 - locate-app: 2.4.26 - safaridriver: 0.1.2 - split2: 4.2.0 - wait-port: 1.1.0 - transitivePeerDependencies: - - supports-color - optional: true - - '@zip.js/zip.js@2.7.48': - optional: true - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - optional: true - - acorn-jsx@5.3.2(acorn@7.4.1): - dependencies: - acorn: 7.4.1 - - acorn@7.4.1: {} - - acorn@8.12.1: {} - - agent-base@7.1.1: - dependencies: - debug: 4.3.6 - transitivePeerDependencies: - - supports-color - optional: true - - ajv-draft-04@1.0.0(ajv@8.13.0): - optionalDependencies: - ajv: 8.13.0 - - ajv-formats@3.0.1(ajv@8.13.0): - optionalDependencies: - ajv: 8.13.0 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.12.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - - ajv@8.13.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - - ajv@8.16.0: - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - - ansi-colors@4.1.3: {} - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.0.1: {} - - ansi-styles@3.2.1: - dependencies: - color-convert: 1.9.3 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.1: {} - - archiver-utils@5.0.2: - dependencies: - glob: 10.4.5 - graceful-fs: 4.2.11 - is-stream: 2.0.1 - lazystream: 1.0.1 - lodash: 4.17.21 - normalize-path: 3.0.0 - readable-stream: 4.5.2 - optional: true - - archiver@7.0.1: - dependencies: - archiver-utils: 5.0.2 - async: 3.2.5 - buffer-crc32: 1.0.0 - readable-stream: 4.5.2 - readdir-glob: 1.1.3 - tar-stream: 3.1.7 - zip-stream: 6.0.1 - optional: true - - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - aria-query@5.1.3: - dependencies: - deep-equal: 2.2.3 - - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 - - array-buffer-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - is-array-buffer: 3.0.4 - - array-includes@3.1.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - is-string: 1.0.7 - - array-union@2.1.0: {} - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.toreversed@1.1.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-shim-unscopables: 1.0.2 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - is-array-buffer: 3.0.4 - is-shared-array-buffer: 1.0.3 - - assertion-error@2.0.1: {} - - ast-types-flow@0.0.8: {} - - ast-types@0.13.4: - dependencies: - tslib: 2.6.3 - optional: true - - astral-regex@2.0.0: {} - - async@3.2.5: - optional: true - - asynckit@0.4.0: - optional: true - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - - axe-core@4.9.1: {} - - axobject-query@3.1.1: - dependencies: - deep-equal: 2.2.3 - - b4a@1.6.6: - optional: true - - balanced-match@1.0.2: {} - - bare-events@2.4.2: - optional: true - - bare-fs@2.3.1: - dependencies: - bare-events: 2.4.2 - bare-path: 2.1.3 - bare-stream: 2.1.3 - optional: true - - bare-os@2.4.0: - optional: true - - bare-path@2.1.3: - dependencies: - bare-os: 2.4.0 - optional: true - - bare-stream@2.1.3: - dependencies: - streamx: 2.18.0 - optional: true - - base64-js@1.5.1: - optional: true - - basic-ftp@5.0.5: - optional: true - - better-path-resolve@1.0.0: - dependencies: - is-windows: 1.0.2 - - boolean@3.2.0: - optional: true - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.23.2: - dependencies: - caniuse-lite: 1.0.30001642 - electron-to-chromium: 1.4.827 - node-releases: 2.0.14 - update-browserslist-db: 1.1.0(browserslist@4.23.2) - - buffer-crc32@0.2.13: {} - - buffer-crc32@1.0.0: - optional: true - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - cac@6.7.14: {} - - cacheable-lookup@5.0.4: {} - - cacheable-lookup@7.0.0: - optional: true - - cacheable-request@10.2.14: - dependencies: - '@types/http-cache-semantics': 4.0.4 - get-stream: 6.0.1 - http-cache-semantics: 4.1.1 - keyv: 4.5.4 - mimic-response: 4.0.0 - normalize-url: 8.0.1 - responselike: 3.0.0 - optional: true - - cacheable-request@7.0.4: - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.1.1 - keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - - caller-callsite@2.0.0: - dependencies: - callsites: 2.0.0 - - caller-path@2.0.0: - dependencies: - caller-callsite: 2.0.0 - - callsites@2.0.0: {} - - callsites@3.1.0: {} - - caniuse-lite@1.0.30001642: {} - - chai@5.1.1: - dependencies: - assertion-error: 2.0.1 - check-error: 2.1.1 - deep-eql: 5.0.2 - loupe: 3.1.1 - pathval: 2.0.0 - - chalk@2.4.2: - dependencies: - ansi-styles: 3.2.1 - escape-string-regexp: 1.0.5 - supports-color: 5.5.0 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@5.3.0: - optional: true - - chardet@0.7.0: {} - - check-error@2.1.1: {} - - chromium-bidi@0.5.8(devtools-protocol@0.0.1232444): - dependencies: - devtools-protocol: 0.0.1232444 - mitt: 3.0.1 - urlpattern-polyfill: 10.0.0 - optional: true - - ci-info@2.0.0: {} - - ci-info@3.9.0: {} - - cli-spinners@2.9.2: {} - - cli-width@4.1.0: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-response@1.0.3: - dependencies: - mimic-response: 1.0.1 - - color-convert@1.9.3: - dependencies: - color-name: 1.1.3 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.3: {} - - color-name@1.1.4: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - optional: true - - commander@9.5.0: - optional: true - - compare-versions@6.1.1: {} - - compress-commons@6.0.2: - dependencies: - crc-32: 1.2.2 - crc32-stream: 6.0.0 - is-stream: 2.0.1 - normalize-path: 3.0.0 - readable-stream: 4.5.2 - optional: true - - computeds@0.0.1: {} - - concat-map@0.0.1: {} - - confbox@0.1.7: {} - - confusing-browser-globals@1.0.11: {} - - convert-source-map@2.0.0: {} - - cookie@0.5.0: {} - - core-util-is@1.0.3: - optional: true - - cosmiconfig@5.2.1: - dependencies: - import-fresh: 2.0.0 - is-directory: 0.3.1 - js-yaml: 3.14.1 - parse-json: 4.0.0 - - crc-32@1.2.2: - optional: true - - crc32-stream@6.0.0: - dependencies: - crc-32: 1.2.2 - readable-stream: 4.5.2 - optional: true - - cross-env@5.2.1: - dependencies: - cross-spawn: 6.0.5 - - cross-fetch@4.0.0: - dependencies: - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - optional: true - - cross-spawn@6.0.5: - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.2 - shebang-command: 1.2.0 - which: 1.3.1 - - cross-spawn@7.0.3: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - css-shorthand-properties@1.1.1: - optional: true - - css-value@0.0.1: - optional: true - - cssstyle@4.0.1: - dependencies: - rrweb-cssom: 0.6.0 - optional: true - - damerau-levenshtein@1.0.8: {} - - data-uri-to-buffer@4.0.1: - optional: true - - data-uri-to-buffer@6.0.2: - optional: true - - data-urls@5.0.0: - dependencies: - whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - optional: true - - data-view-buffer@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - data-view-byte-offset@1.0.0: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-data-view: 1.0.1 - - de-indent@1.0.2: {} - - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.3.4: - dependencies: - ms: 2.1.2 - optional: true - - debug@4.3.5: - dependencies: - ms: 2.1.2 - - debug@4.3.6: - dependencies: - ms: 2.1.2 - - decamelize@6.0.0: - optional: true - - decimal.js@10.4.3: - optional: true - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - deep-eql@5.0.2: {} - - deep-equal@2.2.3: - dependencies: - array-buffer-byte-length: 1.0.1 - call-bind: 1.0.7 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.4 - is-arguments: 1.1.1 - is-array-buffer: 3.0.4 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - isarray: 2.0.5 - object-is: 1.1.6 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - side-channel: 1.0.6 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.2 - which-typed-array: 1.1.15 - - deep-is@0.1.4: {} - - deepmerge-ts@5.1.0: - optional: true - - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - optional: true - - delayed-stream@1.0.0: - optional: true - - dequal@2.0.3: {} - - detect-indent@6.1.0: {} - - detect-node@2.1.0: - optional: true - - devtools-protocol@0.0.1232444: - optional: true - - devtools-protocol@0.0.1335233: - optional: true - - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - doctrine@3.0.0: - dependencies: - esutils: 2.0.3 - - dom-accessibility-api@0.5.16: {} - - eastasianwidth@0.2.0: {} - - edge-paths@3.0.5: - dependencies: - '@types/which': 2.0.2 - which: 2.0.2 - optional: true - - edgedriver@5.6.1: - dependencies: - '@wdio/logger': 8.38.0 - '@zip.js/zip.js': 2.7.48 - decamelize: 6.0.0 - edge-paths: 3.0.5 - fast-xml-parser: 4.4.1 - node-fetch: 3.3.2 - which: 4.0.0 - optional: true - - electron-to-chromium@1.4.827: {} - - electron@31.3.1: - dependencies: - '@electron/get': 2.0.3 - '@types/node': 20.14.10 - extract-zip: 2.0.1 - transitivePeerDependencies: - - supports-color - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - - enquirer@2.4.1: - dependencies: - ansi-colors: 4.1.3 - strip-ansi: 6.0.1 - - entities@4.5.0: {} - - env-paths@2.2.1: {} - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.23.3: - dependencies: - array-buffer-byte-length: 1.0.1 - arraybuffer.prototype.slice: 1.0.3 - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - data-view-buffer: 1.0.1 - data-view-byte-length: 1.0.1 - data-view-byte-offset: 1.0.0 - es-define-property: 1.0.0 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.0.3 - es-to-primitive: 1.2.1 - function.prototype.name: 1.1.6 - get-intrinsic: 1.2.4 - get-symbol-description: 1.0.2 - globalthis: 1.0.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - internal-slot: 1.0.7 - is-array-buffer: 3.0.4 - is-callable: 1.2.7 - is-data-view: 1.0.1 - is-negative-zero: 2.0.3 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.3 - is-string: 1.0.7 - is-typed-array: 1.1.13 - is-weakref: 1.0.2 - object-inspect: 1.13.2 - object-keys: 1.1.1 - object.assign: 4.1.5 - regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.2 - safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.9 - string.prototype.trimend: 1.0.8 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.1 - typed-array-byte-offset: 1.0.2 - typed-array-length: 1.0.6 - unbox-primitive: 1.0.2 - which-typed-array: 1.1.15 - - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - - es-get-iterator@1.1.3: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.3 - is-set: 2.0.3 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - - es-iterator-helpers@1.0.19: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-set-tostringtag: 2.0.3 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - has-property-descriptors: 1.0.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - iterator.prototype: 1.1.2 - safe-array-concat: 1.1.2 - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.0.3: - dependencies: - get-intrinsic: 1.2.4 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.2.1: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.0.5 - is-symbol: 1.0.4 - - es6-error@4.1.1: - optional: true - - esbuild@0.21.5: - optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.23.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 - - escalade@3.1.2: {} - - escape-string-regexp@1.0.5: {} - - escape-string-regexp@4.0.0: {} - - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - optional: true - - eslint-config-airbnb-base@14.2.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint@7.32.0): - dependencies: - confusing-browser-globals: 1.0.11 - eslint: 7.32.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0) - object.assign: 4.1.5 - object.entries: 1.1.8 - - eslint-config-airbnb-typescript@12.3.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.34.3(eslint@7.32.0))(eslint@7.32.0)(typescript@5.5.3): - dependencies: - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.5.3) - eslint-config-airbnb: 18.2.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.34.3(eslint@7.32.0))(eslint@7.32.0) - eslint-config-airbnb-base: 14.2.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint@7.32.0) - transitivePeerDependencies: - - eslint - - eslint-plugin-import - - eslint-plugin-jsx-a11y - - eslint-plugin-react - - eslint-plugin-react-hooks - - supports-color - - typescript - - eslint-config-airbnb@18.2.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint-plugin-jsx-a11y@6.9.0(eslint@7.32.0))(eslint-plugin-react-hooks@4.6.2(eslint@7.32.0))(eslint-plugin-react@7.34.3(eslint@7.32.0))(eslint@7.32.0): - dependencies: - eslint: 7.32.0 - eslint-config-airbnb-base: 14.2.1(eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0))(eslint@7.32.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0) - eslint-plugin-jsx-a11y: 6.9.0(eslint@7.32.0) - eslint-plugin-react: 7.34.3(eslint@7.32.0) - eslint-plugin-react-hooks: 4.6.2(eslint@7.32.0) - object.assign: 4.1.5 - object.entries: 1.1.8 - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.14.0 - resolve: 1.22.8 - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.5.3) - eslint: 7.32.0 - eslint-import-resolver-node: 0.3.9 - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.29.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint@7.32.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.2 - array.prototype.flatmap: 1.3.2 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 7.32.0 - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@4.33.0(eslint@7.32.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint@7.32.0) - hasown: 2.0.2 - is-core-module: 2.14.0 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.0 - semver: 6.3.1 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 4.33.0(eslint@7.32.0)(typescript@5.5.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jsx-a11y@6.9.0(eslint@7.32.0): - dependencies: - aria-query: 5.1.3 - array-includes: 3.1.8 - array.prototype.flatmap: 1.3.2 - ast-types-flow: 0.0.8 - axe-core: 4.9.1 - axobject-query: 3.1.1 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - es-iterator-helpers: 1.0.19 - eslint: 7.32.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.0.3 - string.prototype.includes: 2.0.0 - - eslint-plugin-react-hooks@4.6.2(eslint@7.32.0): - dependencies: - eslint: 7.32.0 - - eslint-plugin-react@7.34.3(eslint@7.32.0): - dependencies: - array-includes: 3.1.8 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.2 - array.prototype.toreversed: 1.1.2 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.0.19 - eslint: 7.32.0 - estraverse: 5.3.0 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.8 - object.fromentries: 2.0.8 - object.hasown: 1.1.4 - object.values: 1.2.0 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.11 - - eslint-scope@5.1.1: - dependencies: - esrecurse: 4.3.0 - estraverse: 4.3.0 - - eslint-utils@2.1.0: - dependencies: - eslint-visitor-keys: 1.3.0 - - eslint-utils@3.0.0(eslint@7.32.0): - dependencies: - eslint: 7.32.0 - eslint-visitor-keys: 2.1.0 - - eslint-visitor-keys@1.3.0: {} - - eslint-visitor-keys@2.1.0: {} - - eslint@7.32.0: - dependencies: - '@babel/code-frame': 7.12.11 - '@eslint/eslintrc': 0.4.3 - '@humanwhocodes/config-array': 0.5.0 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.5 - doctrine: 3.0.0 - enquirer: 2.4.1 - escape-string-regexp: 4.0.0 - eslint-scope: 5.1.1 - eslint-utils: 2.1.0 - eslint-visitor-keys: 2.1.0 - espree: 7.3.1 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 6.0.1 - functional-red-black-tree: 1.0.1 - glob-parent: 5.1.2 - globals: 13.24.0 - ignore: 4.0.6 - import-fresh: 3.3.0 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - js-yaml: 3.14.1 - json-stable-stringify-without-jsonify: 1.0.1 - levn: 0.4.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - progress: 2.0.3 - regexpp: 3.2.0 - semver: 7.6.2 - strip-ansi: 6.0.1 - strip-json-comments: 3.1.1 - table: 6.8.2 - text-table: 0.2.0 - v8-compile-cache: 2.4.0 - transitivePeerDependencies: - - supports-color - - espree@7.3.1: - dependencies: - acorn: 7.4.1 - acorn-jsx: 5.3.2(acorn@7.4.1) - eslint-visitor-keys: 1.3.0 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@4.3.0: {} - - estraverse@5.3.0: {} - - estree-walker@2.0.2: {} - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.5 - - esutils@2.0.3: {} - - event-target-shim@5.0.1: - optional: true - - events@3.3.0: - optional: true - - execa@1.0.0: - dependencies: - cross-spawn: 6.0.5 - get-stream: 4.1.0 - is-stream: 1.1.0 - npm-run-path: 2.0.2 - p-finally: 1.0.0 - signal-exit: 3.0.7 - strip-eof: 1.0.0 - - execa@8.0.1: - dependencies: - cross-spawn: 7.0.3 - get-stream: 8.0.1 - human-signals: 5.0.0 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 4.1.0 - strip-final-newline: 3.0.0 - - extendable-error@0.1.7: {} - - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 - - extract-zip@2.0.1: - dependencies: - debug: 4.3.5 - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - - fast-deep-equal@2.0.1: - optional: true - - fast-deep-equal@3.1.3: {} - - fast-fifo@1.3.2: - optional: true - - fast-glob@3.3.2: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.7 - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-xml-parser@4.4.1: - dependencies: - strnum: 1.0.5 - optional: true - - fastq@1.17.1: - dependencies: - reusify: 1.0.4 - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - optional: true - - file-entry-cache@6.0.1: - dependencies: - flat-cache: 3.2.0 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - find-up@3.0.0: - dependencies: - locate-path: 3.0.0 - - find-up@4.1.0: - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - - flat-cache@3.2.0: - dependencies: - flatted: 3.3.1 - keyv: 4.5.4 - rimraf: 3.0.2 - - flatted@3.3.1: {} - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - - foreground-child@3.2.1: - dependencies: - cross-spawn: 7.0.3 - signal-exit: 4.1.0 - - form-data-encoder@2.1.4: - optional: true - - form-data@4.0.0: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - optional: true - - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - optional: true - - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - optional: true - - fs-extra@7.0.1: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs.realpath@1.0.0: {} - - fsevents@2.3.2: - optional: true - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - function.prototype.name@1.1.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - functions-have-names: 1.2.3 - - functional-red-black-tree@1.0.1: {} - - functions-have-names@1.2.3: {} - - geckodriver@4.4.2: - dependencies: - '@wdio/logger': 8.38.0 - '@zip.js/zip.js': 2.7.48 - decamelize: 6.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - node-fetch: 3.3.2 - tar-fs: 3.0.6 - which: 4.0.0 - transitivePeerDependencies: - - supports-color - optional: true - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-func-name@2.0.2: {} - - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.3 - has-symbols: 1.0.3 - hasown: 2.0.2 - - get-port@7.1.0: - optional: true - - get-stdin@6.0.0: {} - - get-stream@4.1.0: - dependencies: - pump: 3.0.0 - - get-stream@5.2.0: - dependencies: - pump: 3.0.0 - - get-stream@6.0.1: - optional: true - - get-stream@8.0.1: {} - - get-symbol-description@1.0.2: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - - get-tsconfig@4.7.6: - dependencies: - resolve-pkg-maps: 1.0.0 - - get-uri@6.0.3: - dependencies: - basic-ftp: 5.0.5 - data-uri-to-buffer: 6.0.2 - debug: 4.3.6 - fs-extra: 11.2.0 - transitivePeerDependencies: - - supports-color - optional: true - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob@10.4.5: - dependencies: - foreground-child: 3.2.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.0 - path-scurry: 1.11.1 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - global-agent@3.0.0: - dependencies: - boolean: 3.2.0 - es6-error: 4.1.1 - matcher: 3.0.0 - roarr: 2.15.4 - semver: 7.6.2 - serialize-error: 7.0.1 - optional: true - - globals@11.12.0: {} - - globals@13.24.0: - dependencies: - type-fest: 0.20.2 - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.0.1 - - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.2 - ignore: 5.3.1 - merge2: 1.4.1 - slash: 3.0.0 - - gopd@1.0.1: - dependencies: - get-intrinsic: 1.2.4 - - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - got@12.6.1: - dependencies: - '@sindresorhus/is': 5.6.0 - '@szmarczak/http-timer': 5.0.1 - cacheable-lookup: 7.0.0 - cacheable-request: 10.2.14 - decompress-response: 6.0.0 - form-data-encoder: 2.1.4 - get-stream: 6.0.1 - http2-wrapper: 2.2.1 - lowercase-keys: 3.0.0 - p-cancelable: 3.0.0 - responselike: 3.0.0 - optional: true - - graceful-fs@4.2.11: {} - - grapheme-splitter@1.0.4: - optional: true - - graphql@16.9.0: {} - - has-bigints@1.0.2: {} - - has-flag@3.0.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - - has-proto@1.0.3: {} - - has-symbols@1.0.3: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.0.3 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - he@1.2.0: {} - - headers-polyfill@4.0.3: {} - - hosted-git-info@2.8.9: {} - - html-encoding-sniffer@4.0.0: - dependencies: - whatwg-encoding: 3.1.1 - optional: true - - html-escaper@2.0.2: {} - - http-cache-semantics@4.1.1: {} - - http-proxy-agent@7.0.2: - dependencies: - agent-base: 7.1.1 - debug: 4.3.6 - transitivePeerDependencies: - - supports-color - optional: true - - http2-wrapper@1.0.3: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - http2-wrapper@2.2.1: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - optional: true - - https-proxy-agent@7.0.5: - dependencies: - agent-base: 7.1.1 - debug: 4.3.6 - transitivePeerDependencies: - - supports-color - optional: true - - human-id@4.1.1: {} - - human-signals@5.0.0: {} - - husky@1.3.1: - dependencies: - cosmiconfig: 5.2.1 - execa: 1.0.0 - find-up: 3.0.0 - get-stdin: 6.0.0 - is-ci: 2.0.0 - pkg-dir: 3.0.0 - please-upgrade-node: 3.2.0 - read-pkg: 4.0.1 - run-node: 1.0.0 - slash: 2.0.0 - - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - optional: true - - ieee754@1.2.1: - optional: true - - ignore@4.0.6: {} - - ignore@5.3.1: {} - - immediate@3.0.6: - optional: true - - import-fresh@2.0.0: - dependencies: - caller-path: 2.0.0 - resolve-from: 3.0.0 - - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - import-lazy@4.0.0: {} - - import-meta-resolve@4.1.0: - optional: true - - imurmurhash@0.1.4: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - internal-slot@1.0.7: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.0.6 - - ip-address@9.0.5: - dependencies: - jsbn: 1.1.0 - sprintf-js: 1.1.3 - optional: true - - is-arguments@1.1.1: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-array-buffer@3.0.4: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-arrayish@0.2.1: {} - - is-async-function@2.0.0: - dependencies: - has-tostringtag: 1.0.2 - - is-bigint@1.0.4: - dependencies: - has-bigints: 1.0.2 - - is-boolean-object@1.1.2: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-callable@1.2.7: {} - - is-ci@2.0.0: - dependencies: - ci-info: 2.0.0 - - is-core-module@2.14.0: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.1: - dependencies: - is-typed-array: 1.1.13 - - is-date-object@1.0.5: - dependencies: - has-tostringtag: 1.0.2 - - is-directory@0.3.1: {} - - is-extglob@2.1.1: {} - - is-finalizationregistry@1.0.2: - dependencies: - call-bind: 1.0.7 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.0.10: - dependencies: - has-tostringtag: 1.0.2 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-map@2.0.3: {} - - is-negative-zero@2.0.3: {} - - is-node-process@1.2.0: {} - - is-number-object@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-plain-obj@4.1.0: - optional: true - - is-potential-custom-element-name@1.0.1: - optional: true - - is-regex@1.1.4: - dependencies: - call-bind: 1.0.7 - has-tostringtag: 1.0.2 - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.3: - dependencies: - call-bind: 1.0.7 - - is-stream@1.1.0: {} - - is-stream@2.0.1: - optional: true - - is-stream@3.0.0: {} - - is-string@1.0.7: - dependencies: - has-tostringtag: 1.0.2 - - is-subdir@1.2.0: - dependencies: - better-path-resolve: 1.0.0 - - is-symbol@1.0.4: - dependencies: - has-symbols: 1.0.3 - - is-typed-array@1.1.13: - dependencies: - which-typed-array: 1.1.15 - - is-weakmap@2.0.2: {} - - is-weakref@1.0.2: - dependencies: - call-bind: 1.0.7 - - is-weakset@2.0.3: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - - is-windows@1.0.2: {} - - isarray@1.0.0: - optional: true - - isarray@2.0.5: {} - - isexe@2.0.0: {} - - isexe@3.1.1: - optional: true - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-instrument@6.0.3: - dependencies: - '@babel/core': 7.24.8 - '@babel/parser': 7.24.8 - '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.2 - semver: 7.6.2 - transitivePeerDependencies: - - supports-color - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-lib-source-maps@5.0.6: - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.5 - istanbul-lib-coverage: 3.2.2 - transitivePeerDependencies: - - supports-color - - istanbul-reports@3.1.7: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - iterator.prototype@1.1.2: - dependencies: - define-properties: 1.2.1 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - reflect.getprototypeof: 1.0.6 - set-function-name: 2.0.2 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jju@1.4.0: {} - - js-tokens@4.0.0: {} - - js-yaml@3.14.1: - dependencies: - argparse: 1.0.10 - esprima: 4.0.1 - - jsbn@1.1.0: - optional: true - - jsdom@24.1.0: - dependencies: - cssstyle: 4.0.1 - data-urls: 5.0.0 - decimal.js: 10.4.3 - form-data: 4.0.0 - html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.12 - parse5: 7.1.2 - rrweb-cssom: 0.7.1 - saxes: 6.0.0 - symbol-tree: 3.2.4 - tough-cookie: 4.1.4 - w3c-xmlserializer: 5.0.0 - webidl-conversions: 7.0.0 - whatwg-encoding: 3.1.1 - whatwg-mimetype: 4.0.0 - whatwg-url: 14.0.0 - ws: 8.18.0 - xml-name-validator: 5.0.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - optional: true - - jsesc@2.5.2: {} - - json-buffer@3.0.1: {} - - json-parse-better-errors@1.0.2: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json-stringify-safe@5.0.1: - optional: true - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - json5@2.2.3: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - optional: true - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.2 - object.assign: 4.1.5 - object.values: 1.2.0 - - jszip@3.10.1: - dependencies: - lie: 3.3.0 - pako: 1.0.11 - readable-stream: 2.3.8 - setimmediate: 1.0.5 - optional: true - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kolorist@1.8.0: {} - - ky@0.33.3: - optional: true - - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - - lazystream@1.0.1: - dependencies: - readable-stream: 2.3.8 - optional: true - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - lie@3.3.0: - dependencies: - immediate: 3.0.6 - optional: true - - local-pkg@0.5.0: - dependencies: - mlly: 1.7.1 - pkg-types: 1.1.3 - - locate-app@2.4.26: - dependencies: - '@promptbook/utils': 0.63.0 - type-fest: 2.13.0 - userhome: 1.0.0 - optional: true - - locate-path@3.0.0: - dependencies: - p-locate: 3.0.0 - path-exists: 3.0.0 - - locate-path@5.0.0: - dependencies: - p-locate: 4.1.0 - - lodash.clonedeep@4.5.0: - optional: true - - lodash.merge@4.6.2: {} - - lodash.startcase@4.4.0: {} - - lodash.truncate@4.4.2: {} - - lodash.zip@4.2.0: - optional: true - - lodash@4.17.21: {} - - loglevel-plugin-prefix@0.8.4: - optional: true - - loglevel@1.9.1: - optional: true - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - loupe@3.1.1: - dependencies: - get-func-name: 2.0.2 - - lowercase-keys@2.0.0: {} - - lowercase-keys@3.0.0: - optional: true - - lru-cache@10.4.3: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - - lru-cache@7.18.3: - optional: true - - lz-string@1.5.0: {} - - magic-string@0.30.10: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - - magic-string@0.30.11: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - - magicast@0.3.4: - dependencies: - '@babel/parser': 7.24.8 - '@babel/types': 7.24.8 - source-map-js: 1.2.0 - - make-dir@4.0.0: - dependencies: - semver: 7.6.2 - - matcher@3.0.0: - dependencies: - escape-string-regexp: 4.0.0 - optional: true - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - micromatch@4.0.7: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: - optional: true - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - optional: true - - mimic-fn@4.0.0: {} - - mimic-response@1.0.1: {} - - mimic-response@3.1.0: {} - - mimic-response@4.0.0: - optional: true - - minimatch@3.0.8: - dependencies: - brace-expansion: 1.1.11 - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.1 - optional: true - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} - - minipass@7.1.2: {} - - mitt@3.0.1: - optional: true - - mkdirp-classic@0.5.3: - optional: true - - mlly@1.7.1: - dependencies: - acorn: 8.12.1 - pathe: 1.1.2 - pkg-types: 1.1.3 - ufo: 1.5.4 - - mri@1.2.0: {} - - mrmime@2.0.0: {} - - ms@2.1.2: {} - - ms@2.1.3: {} - - msw@2.3.1(typescript@5.5.3): - dependencies: - '@bundled-es-modules/cookie': 2.0.0 - '@bundled-es-modules/statuses': 1.0.1 - '@inquirer/confirm': 3.1.14 - '@mswjs/cookies': 1.1.1 - '@mswjs/interceptors': 0.29.1 - '@open-draft/until': 2.1.0 - '@types/cookie': 0.6.0 - '@types/statuses': 2.0.5 - chalk: 4.1.2 - graphql: 16.9.0 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.2.2 - strict-event-emitter: 0.5.1 - type-fest: 4.21.0 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.5.3 - - muggle-string@0.4.1: {} - - mute-stream@1.0.0: {} - - nanoid@3.3.7: {} - - natural-compare@1.4.0: {} - - netmask@2.0.2: - optional: true - - nice-try@1.0.5: {} + whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} - node-domexception@1.0.0: - optional: true + why-is-node-running@3.2.1: + resolution: {integrity: sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==} + engines: {node: '>=20.11'} + hasBin: true - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - optional: true + ws@8.22.0: + resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - optional: true + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} - node-releases@2.0.14: {} + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} - normalize-package-data@2.5.0: - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.8 - semver: 5.7.2 - validate-npm-package-license: 3.0.4 + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true - normalize-path@3.0.0: - optional: true +snapshots: - normalize-url@6.1.0: {} + '@babel/helper-string-parser@7.29.7': {} - normalize-url@8.0.1: - optional: true + '@babel/helper-validator-identifier@7.29.7': {} - npm-run-path@2.0.2: + '@babel/parser@7.29.9': dependencies: - path-key: 2.0.1 + '@babel/types': 7.29.8 - npm-run-path@5.3.0: + '@babel/types@7.29.8': dependencies: - path-key: 4.0.0 - - nwsapi@2.2.12: - optional: true + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 - object-assign@4.1.1: {} + '@bcoe/v8-coverage@1.0.2': {} - object-inspect@1.13.2: {} + '@blazediff/core@1.10.0': {} - object-is@1.1.6: + '@changesets/apply-release-plan@8.1.1': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - - object-keys@1.1.1: {} + '@changesets/config': 4.0.1 + '@changesets/format': 0.1.2 + '@changesets/git': 4.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + import-meta-resolve: 4.2.0 + jsonc-parser: 3.3.1 + semver: 7.8.5 - object.assign@4.1.5: + '@changesets/assemble-release-plan@7.0.0': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + semver: 7.8.5 - object.entries@1.1.8: + '@changesets/changelog-git@1.0.0': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 + '@changesets/types': 7.0.0 - object.fromentries@2.0.8: + '@changesets/cli@3.0.3': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + '@changesets/apply-release-plan': 8.1.1 + '@changesets/assemble-release-plan': 7.0.0 + '@changesets/changelog-git': 1.0.0 + '@changesets/config': 4.0.1 + '@changesets/errors': 1.0.0 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/git': 4.0.1 + '@changesets/pre': 3.0.0 + '@changesets/read': 1.0.1 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@changesets/write': 1.0.1 + '@clack/prompts': 1.8.1 + '@manypkg/get-packages': 3.1.0 + '@pnpm/deps.graph-sequencer': 1100.0.1 + cac: 7.0.0 + import-meta-resolve: 4.2.0 + launch-editor: 2.14.1 + package-manager-detector: 1.8.0 + semver: 7.8.5 + tinyexec: 1.3.1 - object.groupby@1.0.3: + '@changesets/config@4.0.1': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 + '@changesets/get-dependents-graph': 3.0.0 + '@changesets/should-skip-package': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 - object.hasown@1.1.4: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + '@changesets/errors@1.0.0': {} - object.values@1.2.0: + '@changesets/format@0.1.2': dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 + package-manager-detector: 1.8.0 + tinyexec: 1.3.1 - once@1.4.0: + '@changesets/get-dependents-graph@3.0.0': dependencies: - wrappy: 1.0.2 + '@changesets/types': 7.0.0 + semver: 7.8.5 - onetime@6.0.0: + '@changesets/git@4.0.1': dependencies: - mimic-fn: 4.0.0 + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 + picomatch: 4.0.7 + tinyexec: 1.3.1 - optionator@0.9.4: + '@changesets/parse@1.0.0': dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - os-tmpdir@1.0.2: {} + '@changesets/types': 7.0.0 + yaml: 2.9.1 - outdent@0.5.0: {} - - outvariant@1.4.3: {} - - p-cancelable@2.1.1: {} + '@changesets/pre@3.0.0': + dependencies: + '@changesets/errors': 1.0.0 + '@changesets/types': 7.0.0 + '@manypkg/get-packages': 3.1.0 - p-cancelable@3.0.0: - optional: true + '@changesets/read@1.0.1': + dependencies: + '@changesets/git': 4.0.1 + '@changesets/parse': 1.0.0 + '@changesets/types': 7.0.0 - p-filter@2.1.0: + '@changesets/should-skip-package@1.0.0': dependencies: - p-map: 2.1.0 + '@changesets/types': 7.0.0 - p-finally@1.0.0: {} + '@changesets/types@7.0.0': {} - p-limit@2.3.0: + '@changesets/write@1.0.1': dependencies: - p-try: 2.2.0 + '@changesets/format': 0.1.2 + '@changesets/types': 7.0.0 + human-id: 4.2.1 - p-locate@3.0.0: + '@clack/core@1.5.1': dependencies: - p-limit: 2.3.0 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 - p-locate@4.1.0: + '@clack/prompts@1.8.1': dependencies: - p-limit: 2.3.0 - - p-map@2.1.0: {} + '@clack/core': 1.5.1 + fast-string-width: 3.0.2 + fast-wrap-ansi: 0.2.2 + sisteransi: 1.0.5 - p-try@2.2.0: {} + '@electron-internal/extract-zip@1.0.5': {} - pac-proxy-agent@7.0.2: + '@electron/get@5.1.0(supports-color@8.1.1)': dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.1 - debug: 4.3.6 - get-uri: 6.0.3 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.4 + debug: 4.3.6(supports-color@8.1.1) + env-paths: 3.0.0 + graceful-fs: 4.2.11 + progress: 2.0.3 + semver: 7.8.5 + sumchecker: 3.0.1(supports-color@8.1.1) + optionalDependencies: + undici: 7.30.0 transitivePeerDependencies: - supports-color - optional: true - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.0.2 + '@esbuild/aix-ppc64@0.23.0': optional: true - package-json-from-dist@1.0.0: {} + '@esbuild/android-arm64@0.23.0': + optional: true - package-manager-detector@0.2.11: - dependencies: - quansync: 0.2.10 + '@esbuild/android-arm@0.23.0': + optional: true - pako@1.0.11: + '@esbuild/android-x64@0.23.0': optional: true - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 + '@esbuild/darwin-arm64@0.23.0': + optional: true - parse-json@4.0.0: - dependencies: - error-ex: 1.3.2 - json-parse-better-errors: 1.0.2 + '@esbuild/darwin-x64@0.23.0': + optional: true - parse5@7.1.2: - dependencies: - entities: 4.5.0 + '@esbuild/freebsd-arm64@0.23.0': optional: true - path-browserify@1.0.1: {} + '@esbuild/freebsd-x64@0.23.0': + optional: true - path-exists@3.0.0: {} + '@esbuild/linux-arm64@0.23.0': + optional: true - path-exists@4.0.0: {} + '@esbuild/linux-arm@0.23.0': + optional: true - path-is-absolute@1.0.1: {} + '@esbuild/linux-ia32@0.23.0': + optional: true - path-key@2.0.1: {} + '@esbuild/linux-loong64@0.23.0': + optional: true - path-key@3.1.1: {} + '@esbuild/linux-mips64el@0.23.0': + optional: true - path-key@4.0.0: {} + '@esbuild/linux-ppc64@0.23.0': + optional: true - path-parse@1.0.7: {} + '@esbuild/linux-riscv64@0.23.0': + optional: true - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 + '@esbuild/linux-s390x@0.23.0': + optional: true - path-to-regexp@6.2.2: {} + '@esbuild/linux-x64@0.23.0': + optional: true - path-type@4.0.0: {} + '@esbuild/netbsd-x64@0.23.0': + optional: true - pathe@1.1.2: {} + '@esbuild/openbsd-arm64@0.23.0': + optional: true - pathval@2.0.0: {} + '@esbuild/openbsd-x64@0.23.0': + optional: true - pend@1.2.0: {} + '@esbuild/sunos-x64@0.23.0': + optional: true - picocolors@1.0.1: {} + '@esbuild/win32-arm64@0.23.0': + optional: true - picocolors@1.1.1: {} + '@esbuild/win32-ia32@0.23.0': + optional: true - picomatch@2.3.1: {} + '@esbuild/win32-x64@0.23.0': + optional: true - pify@3.0.0: {} + '@jridgewell/resolve-uri@3.1.2': {} - pify@4.0.1: {} + '@jridgewell/sourcemap-codec@1.5.0': {} - pkg-dir@3.0.0: - dependencies: - find-up: 3.0.0 + '@jridgewell/sourcemap-codec@1.6.0': {} - pkg-types@1.1.3: + '@jridgewell/trace-mapping@0.3.31': dependencies: - confbox: 0.1.7 - mlly: 1.7.1 - pathe: 1.1.2 - - playwright-core@1.45.1: {} + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 - playwright@1.45.1: + '@manypkg/find-root@3.1.0': dependencies: - playwright-core: 1.45.1 - optionalDependencies: - fsevents: 2.3.2 + '@manypkg/tools': 2.1.2 - please-upgrade-node@3.2.0: + '@manypkg/get-packages@3.1.0': dependencies: - semver-compare: 1.0.0 - - possible-typed-array-names@1.0.0: {} + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.2 - postcss@8.4.39: + '@manypkg/tools@2.1.2': dependencies: - nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + jju: 1.4.0 + tinyglobby: 0.2.17 + yaml: 2.9.1 - prelude-ls@1.2.1: {} + '@oxc-project/types@0.152.0': {} - prettier@2.8.8: {} + '@oxlint/binding-android-arm-eabi@1.86.0': + optional: true - pretty-format@27.5.1: - dependencies: - ansi-regex: 5.0.1 - ansi-styles: 5.2.0 - react-is: 17.0.2 + '@oxlint/binding-android-arm64@1.86.0': + optional: true - process-nextick-args@2.0.1: + '@oxlint/binding-darwin-arm64@1.86.0': optional: true - process@0.11.10: + '@oxlint/binding-darwin-x64@1.86.0': optional: true - progress@2.0.3: {} + '@oxlint/binding-freebsd-x64@1.86.0': + optional: true - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 + '@oxlint/binding-linux-arm-gnueabihf@1.86.0': + optional: true - proxy-agent@6.3.1: - dependencies: - agent-base: 7.1.1 - debug: 4.3.6 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.5 - lru-cache: 7.18.3 - pac-proxy-agent: 7.0.2 - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.4 - transitivePeerDependencies: - - supports-color + '@oxlint/binding-linux-arm-musleabihf@1.86.0': optional: true - proxy-from-env@1.1.0: + '@oxlint/binding-linux-arm64-gnu@1.86.0': optional: true - psl@1.9.0: + '@oxlint/binding-linux-arm64-musl@1.86.0': optional: true - pump@3.0.0: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 + '@oxlint/binding-linux-ppc64-gnu@1.86.0': + optional: true - punycode@2.3.1: {} + '@oxlint/binding-linux-riscv64-gnu@1.86.0': + optional: true - puppeteer-core@21.11.0: - dependencies: - '@puppeteer/browsers': 1.9.1 - chromium-bidi: 0.5.8(devtools-protocol@0.0.1232444) - cross-fetch: 4.0.0 - debug: 4.3.4 - devtools-protocol: 0.0.1232444 - ws: 8.16.0 - transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate + '@oxlint/binding-linux-riscv64-musl@1.86.0': optional: true - quansync@0.2.10: {} + '@oxlint/binding-linux-s390x-gnu@1.86.0': + optional: true - query-selector-shadow-dom@1.0.1: + '@oxlint/binding-linux-x64-gnu@1.86.0': optional: true - querystringify@2.2.0: + '@oxlint/binding-linux-x64-musl@1.86.0': optional: true - queue-microtask@1.2.3: {} + '@oxlint/binding-openharmony-arm64@1.86.0': + optional: true - queue-tick@1.0.1: + '@oxlint/binding-win32-arm64-msvc@1.86.0': optional: true - quick-lru@5.1.1: {} + '@oxlint/binding-win32-ia32-msvc@1.86.0': + optional: true - react-is@16.13.1: {} + '@oxlint/binding-win32-x64-msvc@1.86.0': + optional: true - react-is@17.0.2: {} + '@pnpm/deps.graph-sequencer@1100.0.1': {} - read-pkg@4.0.1: - dependencies: - normalize-package-data: 2.5.0 - parse-json: 4.0.0 - pify: 3.0.0 + '@polka/url@1.0.0-next.25': {} - read-yaml-file@1.1.0: - dependencies: - graceful-fs: 4.2.11 - js-yaml: 3.14.1 - pify: 4.0.1 - strip-bom: 3.0.0 + '@rolldown/binding-android-arm-eabi@1.2.12': + optional: true - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 + '@rolldown/binding-android-arm64@1.2.12': optional: true - readable-stream@4.5.2: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 + '@rolldown/binding-darwin-arm64@1.2.12': optional: true - readdir-glob@1.1.3: - dependencies: - minimatch: 5.1.6 + '@rolldown/binding-darwin-x64@1.2.12': optional: true - reflect.getprototypeof@1.0.6: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - globalthis: 1.0.4 - which-builtin-type: 1.1.3 + '@rolldown/binding-freebsd-x64@1.2.12': + optional: true - regenerator-runtime@0.14.1: {} + '@rolldown/binding-linux-arm-gnueabihf@1.2.12': + optional: true - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 + '@rolldown/binding-linux-arm64-gnu@1.2.12': + optional: true - regexpp@3.2.0: {} + '@rolldown/binding-linux-arm64-musl@1.2.12': + optional: true - require-directory@2.1.1: {} + '@rolldown/binding-linux-ppc64-gnu@1.2.12': + optional: true - require-from-string@2.0.2: {} + '@rolldown/binding-linux-s390x-gnu@1.2.12': + optional: true - requires-port@1.0.0: + '@rolldown/binding-linux-x64-gnu@1.2.12': optional: true - resolve-alpn@1.2.1: {} + '@rolldown/binding-linux-x64-musl@1.2.12': + optional: true - resolve-from@3.0.0: {} + '@rolldown/binding-openharmony-arm64@1.2.12': + optional: true - resolve-from@4.0.0: {} + '@rolldown/binding-win32-arm64-msvc@1.2.12': + optional: true - resolve-from@5.0.0: {} + '@rolldown/binding-win32-x64-msvc@1.2.12': + optional: true - resolve-pkg-maps@1.0.0: {} + '@rolldown/pluginutils@1.0.1': {} - resolve@1.22.8: + '@types/chai@5.2.3': dependencies: - is-core-module: 2.14.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.14.0 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 + '@types/deep-eql@4.0.2': {} - responselike@2.0.1: - dependencies: - lowercase-keys: 2.0.0 + '@types/estree@1.0.5': {} - responselike@3.0.0: + '@types/node@24.19.1': dependencies: - lowercase-keys: 3.0.0 - optional: true + undici-types: 7.24.6 - resq@1.11.0: - dependencies: - fast-deep-equal: 2.0.1 + '@typescript/typescript-aix-ppc64@7.0.2': optional: true - reusify@1.0.4: {} + '@typescript/typescript-darwin-arm64@7.0.2': + optional: true - rgb2hex@0.2.5: + '@typescript/typescript-darwin-x64@7.0.2': optional: true - rimraf@3.0.2: - dependencies: - glob: 7.2.3 + '@typescript/typescript-freebsd-arm64@7.0.2': + optional: true - roarr@2.15.4: - dependencies: - boolean: 3.2.0 - detect-node: 2.1.0 - globalthis: 1.0.4 - json-stringify-safe: 5.0.1 - semver-compare: 1.0.0 - sprintf-js: 1.1.3 + '@typescript/typescript-freebsd-x64@7.0.2': optional: true - rollup@4.18.1: - dependencies: - '@types/estree': 1.0.5 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.1 - '@rollup/rollup-android-arm64': 4.18.1 - '@rollup/rollup-darwin-arm64': 4.18.1 - '@rollup/rollup-darwin-x64': 4.18.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.1 - '@rollup/rollup-linux-arm-musleabihf': 4.18.1 - '@rollup/rollup-linux-arm64-gnu': 4.18.1 - '@rollup/rollup-linux-arm64-musl': 4.18.1 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.1 - '@rollup/rollup-linux-riscv64-gnu': 4.18.1 - '@rollup/rollup-linux-s390x-gnu': 4.18.1 - '@rollup/rollup-linux-x64-gnu': 4.18.1 - '@rollup/rollup-linux-x64-musl': 4.18.1 - '@rollup/rollup-win32-arm64-msvc': 4.18.1 - '@rollup/rollup-win32-ia32-msvc': 4.18.1 - '@rollup/rollup-win32-x64-msvc': 4.18.1 - fsevents: 2.3.3 + '@typescript/typescript-linux-arm64@7.0.2': + optional: true - rrweb-cssom@0.6.0: + '@typescript/typescript-linux-arm@7.0.2': optional: true - rrweb-cssom@0.7.1: + '@typescript/typescript-linux-loong64@7.0.2': optional: true - run-node@1.0.0: {} + '@typescript/typescript-linux-mips64el@7.0.2': + optional: true - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 + '@typescript/typescript-linux-ppc64@7.0.2': + optional: true - safaridriver@0.1.2: + '@typescript/typescript-linux-riscv64@7.0.2': optional: true - safe-array-concat@1.1.2: - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - has-symbols: 1.0.3 - isarray: 2.0.5 + '@typescript/typescript-linux-s390x@7.0.2': + optional: true - safe-buffer@5.1.2: + '@typescript/typescript-linux-x64@7.0.2': optional: true - safe-buffer@5.2.1: + '@typescript/typescript-netbsd-arm64@7.0.2': optional: true - safe-regex-test@1.0.3: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-regex: 1.1.4 + '@typescript/typescript-netbsd-x64@7.0.2': + optional: true - safer-buffer@2.1.2: {} + '@typescript/typescript-openbsd-arm64@7.0.2': + optional: true - saxes@6.0.0: - dependencies: - xmlchars: 2.2.0 + '@typescript/typescript-openbsd-x64@7.0.2': optional: true - semver-compare@1.0.0: {} + '@typescript/typescript-sunos-x64@7.0.2': + optional: true - semver@5.7.2: {} + '@typescript/typescript-win32-arm64@7.0.2': + optional: true - semver@6.3.1: {} + '@typescript/typescript-win32-x64@7.0.2': + optional: true - semver@7.5.4: + '@vitest/browser-playwright@5.0.3(playwright@1.63.0)(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3)': dependencies: - lru-cache: 6.0.0 + '@vitest/browser': 5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3) + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + playwright: 1.63.0 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3)': + dependencies: + '@blazediff/core': 1.10.0 + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + '@vitest/ui': 5.0.3(vitest@5.0.3) + '@vitest/utils': 5.0.3 + magic-string: 1.4.2 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + ws: 8.22.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/coverage-v8@5.0.3(@vitest/browser@5.0.3)(vitest@5.0.3)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/istanbul-lib-coverage': 1.0.2 + '@vitest/istanbul-lib-report': 1.0.2 + ast-v8-to-istanbul: 1.0.7 + magicast: 0.5.5 + obug: 2.2.1 + std-env: 4.3.0 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + optionalDependencies: + '@vitest/browser': 5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3) - semver@7.6.2: {} + '@vitest/istanbul-lib-coverage@1.0.2': {} - serialize-error@11.0.3: + '@vitest/istanbul-lib-report@1.0.2': dependencies: - type-fest: 2.19.0 - optional: true + '@vitest/istanbul-lib-coverage': 1.0.2 - serialize-error@7.0.1: + '@vitest/mocker@5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))': dependencies: - type-fest: 0.13.1 - optional: true + '@jridgewell/trace-mapping': 0.3.31 + '@vitest/spy': 5.0.3 + estree-walker: 3.0.3 + magic-string: 1.4.2 + optionalDependencies: + vite: 8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1) - set-function-length@1.2.2: + '@vitest/pretty-format@5.0.3': dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 + tinyrainbow: 3.2.0 - setimmediate@1.0.5: - optional: true + '@vitest/spy@5.0.3': {} - shebang-command@1.2.0: + '@vitest/ui@5.0.3(vitest@5.0.3)': dependencies: - shebang-regex: 1.0.0 + '@vitest/utils': 5.0.3 + fflate: 0.8.3 + flatted: 3.4.4 + pathe: 2.0.3 + sirv: 3.0.2 + tinyrainbow: 3.2.0 + vitest: 5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) - shebang-command@2.0.0: + '@vitest/utils@5.0.3': dependencies: - shebang-regex: 3.0.0 - - shebang-regex@1.0.0: {} + '@vitest/pretty-format': 5.0.3 + convert-source-map: 2.0.0 + tinyrainbow: 3.2.0 - shebang-regex@3.0.0: {} + acorn@8.18.0: {} - side-channel@1.0.6: + agent-base@7.1.1(supports-color@8.1.1): dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.2 - - siginfo@2.0.0: {} - - signal-exit@3.0.7: {} + debug: 4.3.6(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + optional: true - signal-exit@4.1.0: {} + assertion-error@2.0.1: {} - sirv@2.0.4: + ast-v8-to-istanbul@1.0.7: dependencies: - '@polka/url': 1.0.0-next.25 - mrmime: 2.0.0 - totalist: 3.0.1 + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + asynckit@0.4.0: + optional: true - slash@2.0.0: {} + cac@7.0.0: {} - slash@3.0.0: {} + chai@6.3.0: {} - slice-ansi@4.0.0: + combined-stream@1.0.8: dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - - smart-buffer@4.2.0: + delayed-stream: 1.0.0 optional: true - socks-proxy-agent@8.0.4: + convert-source-map@2.0.0: {} + + cssstyle@4.0.1: dependencies: - agent-base: 7.1.1 - debug: 4.3.6 - socks: 2.8.3 - transitivePeerDependencies: - - supports-color + rrweb-cssom: 0.6.0 optional: true - socks@2.8.3: + data-urls@5.0.0: dependencies: - ip-address: 9.0.5 - smart-buffer: 4.2.0 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 optional: true - source-map-js@1.2.0: {} - - source-map@0.6.1: {} + debug@4.3.6(supports-color@8.1.1): + dependencies: + ms: 2.1.2 + optionalDependencies: + supports-color: 8.1.1 - spacetrim@0.11.39: + decimal.js@10.4.3: optional: true - spawndamnit@3.0.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - spdx-correct@3.2.0: - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.18 + delayed-stream@1.0.0: + optional: true - spdx-exceptions@2.5.0: {} + detect-libc@2.1.2: {} - spdx-expression-parse@3.0.1: + electron@44.5.1(supports-color@8.1.1): dependencies: - spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.18 - - spdx-license-ids@3.0.18: {} + '@electron-internal/extract-zip': 1.0.5 + '@electron/get': 5.1.0(supports-color@8.1.1) + '@types/node': 24.19.1 + transitivePeerDependencies: + - supports-color - split2@4.2.0: + entities@4.5.0: optional: true - sprintf-js@1.0.3: {} + env-paths@3.0.0: {} - sprintf-js@1.1.3: + es-module-lexer@2.3.2: {} + + esbuild@0.23.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.0 + '@esbuild/android-arm': 0.23.0 + '@esbuild/android-arm64': 0.23.0 + '@esbuild/android-x64': 0.23.0 + '@esbuild/darwin-arm64': 0.23.0 + '@esbuild/darwin-x64': 0.23.0 + '@esbuild/freebsd-arm64': 0.23.0 + '@esbuild/freebsd-x64': 0.23.0 + '@esbuild/linux-arm': 0.23.0 + '@esbuild/linux-arm64': 0.23.0 + '@esbuild/linux-ia32': 0.23.0 + '@esbuild/linux-loong64': 0.23.0 + '@esbuild/linux-mips64el': 0.23.0 + '@esbuild/linux-ppc64': 0.23.0 + '@esbuild/linux-riscv64': 0.23.0 + '@esbuild/linux-s390x': 0.23.0 + '@esbuild/linux-x64': 0.23.0 + '@esbuild/netbsd-x64': 0.23.0 + '@esbuild/openbsd-arm64': 0.23.0 + '@esbuild/openbsd-x64': 0.23.0 + '@esbuild/sunos-x64': 0.23.0 + '@esbuild/win32-arm64': 0.23.0 + '@esbuild/win32-ia32': 0.23.0 + '@esbuild/win32-x64': 0.23.0 optional: true - stackback@0.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 - statuses@2.0.1: {} + expect-type@1.4.0: {} - std-env@3.7.0: {} + fast-string-truncated-width@3.0.3: {} - stop-iteration-iterator@1.0.0: + fast-string-width@3.0.2: dependencies: - internal-slot: 1.0.7 + fast-string-truncated-width: 3.0.3 - streamx@2.18.0: + fast-wrap-ansi@0.2.2: dependencies: - fast-fifo: 1.3.2 - queue-tick: 1.0.1 - text-decoder: 1.1.1 + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.7): optionalDependencies: - bare-events: 2.4.2 - optional: true + picomatch: 4.0.7 - strict-event-emitter@0.5.1: {} + fflate@0.8.3: {} - string-argv@0.3.2: {} + flatted@3.4.4: {} - string-width@4.2.3: + form-data@4.0.0: dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + optional: true - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + fsevents@2.3.3: + optional: true - string.prototype.includes@2.0.0: + get-tsconfig@4.7.6: dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.3 + resolve-pkg-maps: 1.0.0 + optional: true - string.prototype.matchall@4.0.11: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-symbols: 1.0.3 - internal-slot: 1.0.7 - regexp.prototype.flags: 1.5.2 - set-function-name: 2.0.2 - side-channel: 1.0.6 - - string.prototype.trim@1.2.9: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.23.3 - es-object-atoms: 1.0.0 + graceful-fs@4.2.11: {} - string.prototype.trimend@1.0.8: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 + has-flag@4.0.0: + optional: true - string.prototype.trimstart@1.0.8: + html-encoding-sniffer@4.0.0: dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 + whatwg-encoding: 3.1.1 + optional: true - string_decoder@1.1.1: + http-proxy-agent@7.0.2(supports-color@8.1.1): dependencies: - safe-buffer: 5.1.2 + agent-base: 7.1.1(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color optional: true - string_decoder@1.3.0: + https-proxy-agent@7.0.5(supports-color@8.1.1): dependencies: - safe-buffer: 5.2.1 + agent-base: 7.1.1(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color optional: true - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 + human-id@4.2.1: {} - strip-ansi@7.1.0: + iconv-lite@0.6.3: dependencies: - ansi-regex: 6.0.1 - - strip-bom@3.0.0: {} + safer-buffer: 2.1.2 + optional: true - strip-eof@1.0.0: {} + import-meta-resolve@4.2.0: {} - strip-final-newline@3.0.0: {} + is-potential-custom-element-name@1.0.1: + optional: true - strip-json-comments@3.1.1: {} + jju@1.4.0: {} - strnum@1.0.5: - optional: true + js-tokens@10.0.0: {} - sumchecker@3.0.1: + jsdom@24.1.0(supports-color@8.1.1): dependencies: - debug: 4.3.5 + cssstyle: 4.0.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2(supports-color@8.1.1) + https-proxy-agent: 7.0.5(supports-color@8.1.1) + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.12 + parse5: 7.1.2 + rrweb-cssom: 0.7.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.22.0 + xml-name-validator: 5.0.0 transitivePeerDependencies: + - bufferutil - supports-color + - utf-8-validate + optional: true - supports-color@5.5.0: - dependencies: - has-flag: 3.0.0 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 + jsonc-parser@3.3.1: {} - supports-color@8.1.1: + launch-editor@2.14.1: dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} + picocolors: 1.1.1 + shell-quote: 1.11.0 - symbol-tree@3.2.4: + lightningcss-android-arm64@1.33.0: optional: true - table@6.8.2: - dependencies: - ajv: 8.16.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - tar-fs@3.0.4: - dependencies: - mkdirp-classic: 0.5.3 - pump: 3.0.0 - tar-stream: 3.1.7 + lightningcss-darwin-arm64@1.33.0: optional: true - tar-fs@3.0.6: - dependencies: - pump: 3.0.0 - tar-stream: 3.1.7 - optionalDependencies: - bare-fs: 2.3.1 - bare-path: 2.1.3 + lightningcss-darwin-x64@1.33.0: optional: true - tar-stream@3.1.7: - dependencies: - b4a: 1.6.6 - fast-fifo: 1.3.2 - streamx: 2.18.0 + lightningcss-freebsd-x64@1.33.0: optional: true - term-size@2.2.1: {} - - test-exclude@7.0.1: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 - minimatch: 9.0.5 - - text-decoder@1.1.1: - dependencies: - b4a: 1.6.6 + lightningcss-linux-arm-gnueabihf@1.33.0: optional: true - text-table@0.2.0: {} + lightningcss-linux-arm64-gnu@1.33.0: + optional: true - through@2.3.8: + lightningcss-linux-arm64-musl@1.33.0: optional: true - tinybench@2.8.0: {} + lightningcss-linux-x64-gnu@1.33.0: + optional: true - tinypool@1.0.0: {} + lightningcss-linux-x64-musl@1.33.0: + optional: true - tinyrainbow@1.2.0: {} + lightningcss-win32-arm64-msvc@1.33.0: + optional: true - tinyspy@3.0.0: {} + lightningcss-win32-x64-msvc@1.33.0: + optional: true - tmp@0.0.33: + lightningcss@1.33.0: dependencies: - os-tmpdir: 1.0.2 - - to-fast-properties@2.0.0: {} + detect-libc: 2.1.2 + 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 - to-regex-range@5.0.1: + magic-string@1.4.2: dependencies: - is-number: 7.0.0 - - totalist@3.0.1: {} + '@jridgewell/sourcemap-codec': 1.6.0 - tough-cookie@4.1.4: + magicast@0.5.5: dependencies: - psl: 1.9.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 - optional: true + '@babel/parser': 7.29.9 + '@babel/types': 7.29.8 + source-map-js: 1.2.2 - tr46@0.0.3: + mime-db@1.52.0: optional: true - tr46@5.0.0: + mime-types@2.1.35: dependencies: - punycode: 2.3.1 + mime-db: 1.52.0 optional: true - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 + mrmime@2.0.0: {} - tslib@1.14.1: {} + ms@2.1.2: {} + + nanoid@3.3.19: {} - tslib@2.6.3: + nwsapi@2.2.12: optional: true - tsutils@3.21.0(typescript@5.5.3): - dependencies: - tslib: 1.14.1 - typescript: 5.5.3 + obug@2.2.1: {} - tsx@4.17.0: - dependencies: - esbuild: 0.23.0 - get-tsconfig: 4.7.6 + oxlint@1.86.0: optionalDependencies: - fsevents: 2.3.3 + '@oxlint/binding-android-arm-eabi': 1.86.0 + '@oxlint/binding-android-arm64': 1.86.0 + '@oxlint/binding-darwin-arm64': 1.86.0 + '@oxlint/binding-darwin-x64': 1.86.0 + '@oxlint/binding-freebsd-x64': 1.86.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.86.0 + '@oxlint/binding-linux-arm-musleabihf': 1.86.0 + '@oxlint/binding-linux-arm64-gnu': 1.86.0 + '@oxlint/binding-linux-arm64-musl': 1.86.0 + '@oxlint/binding-linux-ppc64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-gnu': 1.86.0 + '@oxlint/binding-linux-riscv64-musl': 1.86.0 + '@oxlint/binding-linux-s390x-gnu': 1.86.0 + '@oxlint/binding-linux-x64-gnu': 1.86.0 + '@oxlint/binding-linux-x64-musl': 1.86.0 + '@oxlint/binding-openharmony-arm64': 1.86.0 + '@oxlint/binding-win32-arm64-msvc': 1.86.0 + '@oxlint/binding-win32-ia32-msvc': 1.86.0 + '@oxlint/binding-win32-x64-msvc': 1.86.0 + + package-manager-detector@1.8.0: {} - type-check@0.4.0: + parse5@7.1.2: dependencies: - prelude-ls: 1.2.1 - - type-fest@0.13.1: + entities: 4.5.0 optional: true - type-fest@0.20.2: {} + pathe@2.0.3: {} - type-fest@0.21.3: {} - - type-fest@2.13.0: - optional: true + picocolors@1.1.1: {} - type-fest@2.19.0: - optional: true + picomatch@4.0.7: {} - type-fest@4.21.0: {} + playwright-core@1.63.0: {} - typed-array-buffer@1.0.2: + playwright@1.63.0: dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - is-typed-array: 1.1.13 + playwright-core: 1.63.0 - typed-array-byte-length@1.0.1: - dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 + pngjs@7.0.0: {} - typed-array-byte-offset@1.0.2: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - - typed-array-length@1.0.6: + postcss@8.5.28: dependencies: - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-proto: 1.0.3 - is-typed-array: 1.1.13 - possible-typed-array-names: 1.0.0 - - typescript@5.5.3: {} - - ufo@1.5.4: {} + nanoid: 3.3.19 + picocolors: 1.1.1 + source-map-js: 1.2.2 - unbox-primitive@1.0.2: - dependencies: - call-bind: 1.0.7 - has-bigints: 1.0.2 - has-symbols: 1.0.3 - which-boxed-primitive: 1.0.2 + progress@2.0.3: {} - unbzip2-stream@1.4.3: - dependencies: - buffer: 5.7.1 - through: 2.3.8 + psl@1.9.0: optional: true - undici-types@5.26.5: {} - - undici-types@6.18.2: {} - - universalify@0.1.2: {} + punycode@2.3.1: + optional: true - universalify@0.2.0: + querystringify@2.2.0: optional: true - universalify@2.0.1: + requires-port@1.0.0: optional: true - update-browserslist-db@1.1.0(browserslist@4.23.2): - dependencies: - browserslist: 4.23.2 - escalade: 3.1.2 - picocolors: 1.0.1 + resolve-pkg-maps@1.0.0: + optional: true - uri-js@4.4.1: + rolldown@1.2.12: dependencies: - punycode: 2.3.1 + '@oxc-project/types': 0.152.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.12 + '@rolldown/binding-android-arm64': 1.2.12 + '@rolldown/binding-darwin-arm64': 1.2.12 + '@rolldown/binding-darwin-x64': 1.2.12 + '@rolldown/binding-freebsd-x64': 1.2.12 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.12 + '@rolldown/binding-linux-arm64-gnu': 1.2.12 + '@rolldown/binding-linux-arm64-musl': 1.2.12 + '@rolldown/binding-linux-ppc64-gnu': 1.2.12 + '@rolldown/binding-linux-s390x-gnu': 1.2.12 + '@rolldown/binding-linux-x64-gnu': 1.2.12 + '@rolldown/binding-linux-x64-musl': 1.2.12 + '@rolldown/binding-openharmony-arm64': 1.2.12 + '@rolldown/binding-win32-arm64-msvc': 1.2.12 + '@rolldown/binding-win32-x64-msvc': 1.2.12 - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 + rrweb-cssom@0.6.0: optional: true - urlpattern-polyfill@10.0.0: + rrweb-cssom@0.7.1: optional: true - userhome@1.0.0: + safer-buffer@2.1.2: optional: true - util-deprecate@1.0.2: + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 optional: true - v8-compile-cache@2.4.0: {} + semver@7.8.5: {} - validate-npm-package-license@3.0.4: - dependencies: - spdx-correct: 3.2.0 - spdx-expression-parse: 3.0.1 + shell-quote@1.11.0: {} - vite-node@2.0.2(@types/node@22.3.0): + sirv@3.0.2: dependencies: - cac: 6.7.14 - debug: 4.3.5 - pathe: 1.1.2 - tinyrainbow: 1.2.0 - vite: 5.3.3(@types/node@22.3.0) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser + '@polka/url': 1.0.0-next.25 + mrmime: 2.0.0 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + source-map-js@1.2.2: {} + + std-env@4.3.0: {} - vite-plugin-dts@4.0.3(@types/node@22.3.0)(rollup@4.18.1)(typescript@5.5.3)(vite@5.3.3(@types/node@22.3.0)): + sumchecker@3.0.1(supports-color@8.1.1): dependencies: - '@microsoft/api-extractor': 7.47.4(@types/node@22.3.0) - '@rollup/pluginutils': 5.1.0(rollup@4.18.1) - '@volar/typescript': 2.3.4 - '@vue/language-core': 2.0.29(typescript@5.5.3) - compare-versions: 6.1.1 - debug: 4.3.6 - kolorist: 1.8.0 - local-pkg: 0.5.0 - magic-string: 0.30.11 - typescript: 5.5.3 - vue-tsc: 2.0.29(typescript@5.5.3) - optionalDependencies: - vite: 5.3.3(@types/node@22.3.0) + debug: 4.3.6(supports-color@8.1.1) transitivePeerDependencies: - - '@types/node' - - rollup - supports-color - vite@5.3.3(@types/node@22.3.0): + supports-color@8.1.1: dependencies: - esbuild: 0.21.5 - postcss: 8.4.39 - rollup: 4.18.1 - optionalDependencies: - '@types/node': 22.3.0 - fsevents: 2.3.3 + has-flag: 4.0.0 + optional: true + + symbol-tree@3.2.4: + optional: true + + tinybench@6.2.0: {} - vitest@2.0.2(@types/node@22.3.0)(@vitest/browser@2.0.2)(jsdom@24.1.0): + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: dependencies: - '@ampproject/remapping': 2.3.0 - '@vitest/expect': 2.0.2 - '@vitest/pretty-format': 2.0.2 - '@vitest/runner': 2.0.2 - '@vitest/snapshot': 2.0.2 - '@vitest/spy': 2.0.2 - '@vitest/utils': 2.0.2 - chai: 5.1.1 - debug: 4.3.5 - execa: 8.0.1 - magic-string: 0.30.10 - pathe: 1.1.2 - std-env: 3.7.0 - tinybench: 2.8.0 - tinypool: 1.0.0 - tinyrainbow: 1.2.0 - vite: 5.3.3(@types/node@22.3.0) - vite-node: 2.0.2(@types/node@22.3.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 22.3.0 - '@vitest/browser': 2.0.2(playwright@1.45.1)(typescript@5.5.3)(vitest@2.0.2)(webdriverio@8.40.2) - jsdom: 24.1.0 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 - vscode-uri@3.0.8: {} + tinyrainbow@3.2.0: {} + + totalist@3.0.1: {} - vue-tsc@2.0.29(typescript@5.5.3): + tough-cookie@4.1.4: dependencies: - '@volar/typescript': 2.4.0-alpha.18 - '@vue/language-core': 2.0.29(typescript@5.5.3) - semver: 7.6.2 - typescript: 5.5.3 + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + optional: true - w3c-xmlserializer@5.0.0: + tr46@5.0.0: dependencies: - xml-name-validator: 5.0.0 + punycode: 2.3.1 optional: true - wait-port@1.1.0: + tsx@4.17.0: dependencies: - chalk: 4.1.2 - commander: 9.5.0 - debug: 4.3.6 - transitivePeerDependencies: - - supports-color + esbuild: 0.23.0 + get-tsconfig: 4.7.6 + optionalDependencies: + fsevents: 2.3.3 + optional: true + + typescript@7.0.2: + optionalDependencies: + '@typescript/typescript-aix-ppc64': 7.0.2 + '@typescript/typescript-darwin-arm64': 7.0.2 + '@typescript/typescript-darwin-x64': 7.0.2 + '@typescript/typescript-freebsd-arm64': 7.0.2 + '@typescript/typescript-freebsd-x64': 7.0.2 + '@typescript/typescript-linux-arm': 7.0.2 + '@typescript/typescript-linux-arm64': 7.0.2 + '@typescript/typescript-linux-loong64': 7.0.2 + '@typescript/typescript-linux-mips64el': 7.0.2 + '@typescript/typescript-linux-ppc64': 7.0.2 + '@typescript/typescript-linux-riscv64': 7.0.2 + '@typescript/typescript-linux-s390x': 7.0.2 + '@typescript/typescript-linux-x64': 7.0.2 + '@typescript/typescript-netbsd-arm64': 7.0.2 + '@typescript/typescript-netbsd-x64': 7.0.2 + '@typescript/typescript-openbsd-arm64': 7.0.2 + '@typescript/typescript-openbsd-x64': 7.0.2 + '@typescript/typescript-sunos-x64': 7.0.2 + '@typescript/typescript-win32-arm64': 7.0.2 + '@typescript/typescript-win32-x64': 7.0.2 + + undici-types@7.24.6: {} + + undici@7.30.0: optional: true - web-streams-polyfill@3.3.3: + universalify@0.2.0: optional: true - webdriver@8.40.2: + url-parse@1.5.10: dependencies: - '@types/node': 20.14.10 - '@types/ws': 8.5.12 - '@wdio/config': 8.40.2 - '@wdio/logger': 8.38.0 - '@wdio/protocols': 8.38.0 - '@wdio/types': 8.39.0 - '@wdio/utils': 8.40.2 - deepmerge-ts: 5.1.0 - got: 12.6.1 - ky: 0.33.3 - ws: 8.18.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate + querystringify: 2.2.0 + requires-port: 1.0.0 optional: true - webdriverio@8.40.2: + vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1): dependencies: - '@types/node': 20.14.10 - '@wdio/config': 8.40.2 - '@wdio/logger': 8.38.0 - '@wdio/protocols': 8.38.0 - '@wdio/repl': 8.24.12 - '@wdio/types': 8.39.0 - '@wdio/utils': 8.40.2 - archiver: 7.0.1 - aria-query: 5.3.0 - css-shorthand-properties: 1.1.1 - css-value: 0.0.1 - devtools-protocol: 0.0.1335233 - grapheme-splitter: 1.0.4 - import-meta-resolve: 4.1.0 - is-plain-obj: 4.1.0 - jszip: 3.10.1 - lodash.clonedeep: 4.5.0 - lodash.zip: 4.2.0 - minimatch: 9.0.5 - puppeteer-core: 21.11.0 - query-selector-shadow-dom: 1.0.1 - resq: 1.11.0 - rgb2hex: 0.2.5 - serialize-error: 11.0.3 - webdriver: 8.40.2 + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.12 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.19.1 + fsevents: 2.3.3 + tsx: 4.17.0 + yaml: 2.9.1 + + vitest@5.0.3(@types/node@24.19.1)(@vitest/browser-playwright@5.0.3)(@vitest/coverage-v8@5.0.3)(@vitest/ui@5.0.3)(jsdom@24.1.0(supports-color@8.1.1))(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)): + dependencies: + '@types/chai': 5.2.3 + '@vitest/mocker': 5.0.3(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1)) + chai: 6.3.0 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 1.4.2 + obug: 2.2.1 + picomatch: 4.0.7 + std-env: 4.3.0 + tinybench: 6.2.0 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + vite: 8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1) + why-is-node-running: 3.2.1 + optionalDependencies: + '@types/node': 24.19.1 + '@vitest/browser-playwright': 5.0.3(playwright@1.63.0)(vite@8.3.2(@types/node@24.19.1)(tsx@4.17.0)(yaml@2.9.1))(vitest@5.0.3) + '@vitest/coverage-v8': 5.0.3(@vitest/browser@5.0.3)(vitest@5.0.3) + '@vitest/ui': 5.0.3(vitest@5.0.3) + jsdom: 24.1.0(supports-color@8.1.1) transitivePeerDependencies: - - bufferutil - - encoding - - supports-color - - utf-8-validate - optional: true + - msw - webidl-conversions@3.0.1: + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 optional: true webidl-conversions@7.0.0: @@ -8147,94 +2595,9 @@ snapshots: webidl-conversions: 7.0.0 optional: true - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - optional: true - - which-boxed-primitive@1.0.2: - dependencies: - is-bigint: 1.0.4 - is-boolean-object: 1.1.2 - is-number-object: 1.0.7 - is-string: 1.0.7 - is-symbol: 1.0.4 - - which-builtin-type@1.1.3: - dependencies: - function.prototype.name: 1.1.6 - has-tostringtag: 1.0.2 - is-async-function: 2.0.0 - is-date-object: 1.0.5 - is-finalizationregistry: 1.0.2 - is-generator-function: 1.0.10 - is-regex: 1.1.4 - is-weakref: 1.0.2 - isarray: 2.0.5 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.2 - which-typed-array: 1.1.15 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.3 - - which-typed-array@1.1.15: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.7 - for-each: 0.3.3 - gopd: 1.0.1 - has-tostringtag: 1.0.2 - - which@1.3.1: - dependencies: - isexe: 2.0.0 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - which@4.0.0: - dependencies: - isexe: 3.1.1 - optional: true - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - word-wrap@1.2.5: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - wrappy@1.0.2: {} + why-is-node-running@3.2.1: {} - ws@8.16.0: - optional: true - - ws@8.18.0: {} + ws@8.22.0: {} xml-name-validator@5.0.0: optional: true @@ -8242,34 +2605,4 @@ snapshots: xmlchars@2.2.0: optional: true - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yallist@4.0.0: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.1.2 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - - yoctocolors-cjs@2.1.2: {} - - zip-stream@6.0.1: - dependencies: - archiver-utils: 5.0.2 - compress-commons: 6.0.2 - readable-stream: 4.5.2 - optional: true + yaml@2.9.1: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 36b35c2..905db98 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,9 +2,14 @@ packages: - 'packages/*' catalog: - vite: ^5.3.3 - vitest: ^2.0.0 - vite-plugin-dts: ^4.0.2 - typescript: ^5.5.0 - electron: ^31.0.0 - jsdom: ^24.0.0 + vite: 8.3.2 + vitest: 5.0.3 + typescript: 7.0.2 + electron: 44.5.1 + '@vitest/browser-playwright': 5.0.3 + '@vitest/coverage-v8': 5.0.3 + playwright: 1.63.0 + +allowBuilds: + electron: true + esbuild: true diff --git a/scripts/benchmark-rpc.mjs b/scripts/benchmark-rpc.mjs new file mode 100644 index 0000000..e63f43b --- /dev/null +++ b/scripts/benchmark-rpc.mjs @@ -0,0 +1,166 @@ +import assert from 'node:assert/strict' +import http from 'node:http' +import { readFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' + +// Usage: node scripts/benchmark-rpc.mjs /path/to/2.1.0/dist/index.production.es.js [previous-build.mjs] +const root = fileURLToPath(new URL('../', import.meta.url)) +const require = createRequire(new URL('../packages/duplex-message/package.json', import.meta.url)) +const { chromium } = require('playwright') +assert.ok(process.argv[2], 'Supply the unpacked baseline production ESM file.') +const files = { + published: process.argv[2], + ...(process.argv[3] ? { previous: process.argv[3] } : {}), + current: `${root}packages/duplex-message/dist/index.production.es.mjs`, +} +const sources = Object.fromEntries(await Promise.all(Object.entries(files).map(async ([name, path]) => [ + `/${name}.mjs`, await readFile(path), +]))) +const server = http.createServer((request, response) => { + const source = sources[request.url] + response.setHeader('content-type', source ? 'text/javascript' : 'text/html') + response.end(source ?? 'RPC benchmark') +}) +await new Promise(resolve => server.listen(0, '127.0.0.1', resolve)) +const origin = `http://127.0.0.1:${server.address().port}` +let browser +try { + browser = await chromium.launch({ headless: true }) + const page = await browser.newPage() + page.on('console', message => console.error(message.text())) + await page.goto(origin) + const results = await page.evaluate(async ({ origin, versions }) => { + const modules = Object.fromEntries(await Promise.all(versions.map(async version => [ + version, await import(`${origin}/${version}.mjs`), + ]))) + const pause = () => new Promise(resolve => setTimeout(resolve, 550)) + const median = values => { + const sorted = [...values].sort((a, b) => a - b) + return (sorted[3] + sorted[4]) / 2 + } + const results = [] + for (const scenario of ['clone-transport', 'worker-sequential', 'worker-32-concurrent']) { + const peers = {} + for (const version of versions) { + if (scenario === 'clone-transport') { + class Hub extends modules[version].AbstractHub { + on(peer, name, fn) { this._on(peer, name, fn) } + off(peer) { this._off(peer) } + emit(peer, name, ...args) { return this._emit(peer, name, ...args) } + sendMessage(peer, message) { + const copy = structuredClone(message) + queueMicrotask(() => { void peer.onMessage(this, copy) }) + } + } + const hub = new Hub(), peer = new Hub() + peer.on(hub, 'echo', value => value) + peers[version] = { hub, peer, close: () => { hub.destroy(); peer.destroy() }, samples: [] } + } else { + const source = `import {PostMessageHub} from '${origin}/${version}.mjs'; + const hub = new PostMessageHub(); hub.on(self, 'echo', value => value); self.postMessage('ready');` + const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' })) + const peer = new Worker(url, { type: 'module' }) + await new Promise((resolve, reject) => { + peer.onmessage = event => { if (event.data === 'ready') resolve() } + peer.onerror = reject + }) + const hub = new modules[version].PostMessageHub() + peers[version] = { hub, peer, close: () => { hub.destroy(); peer.terminate(); URL.revokeObjectURL(url) }, samples: [] } + } + const { hub, peer } = peers[version] + for (let i = 0; i < 1000; i++) await hub.emit(peer, 'echo', i) + } + await pause() + const count = scenario === 'worker-32-concurrent' ? 16000 : 5000 + const concurrency = scenario === 'worker-32-concurrent' ? 32 : 1 + for (let round = 0; round < 8; round++) { + const order = versions.map((_, i) => versions[(i + round) % versions.length]) + for (const version of order) { + const { hub, peer, samples } = peers[version] + let next = 0 + const start = performance.now() + await Promise.all(Array.from({ length: concurrency }, async () => { + while (next < count) { + const value = next++ + if (await hub.emit(peer, 'echo', value) !== value) throw new Error('incorrect result') + } + })) + samples.push((performance.now() - start) * 1000 / count) + // Do not let baseline timers expire during another version's measured run. + await pause() + } + } + for (const version of versions) { + const { samples, close } = peers[version] + close() + results.push({ scenario, version, medianUsPerCall: median(samples), samplesUsPerCall: samples }) + } + console.log(`${scenario}: complete`) + } + return results + }, { origin, versions: Object.keys(files) }) + await page.close() + // Isolate each continuous-load run: the old version's uncancelled timers must not + // expire in the next version's renderer or be hidden by pauses between rounds. + const versions = Object.keys(files) + for (const concurrency of [1, 32]) { + const aggregate = Object.fromEntries(versions.map(version => [version, []])) + for (let cycle = 0; cycle < 3; cycle++) { + for (const version of versions.map((_, i) => versions[(i + cycle) % versions.length])) { + const context = await browser.newContext() + const steadyPage = await context.newPage() + try { + await steadyPage.goto(origin) + const samples = await steadyPage.evaluate(async ({ origin, version, concurrency }) => { + const { PostMessageHub } = await import(`${origin}/${version}.mjs`) + const source = `import {PostMessageHub} from '${origin}/${version}.mjs'; + const hub=new PostMessageHub(); hub.on(self,'echo',value=>value); self.postMessage('ready');` + const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' })) + const peer = new Worker(url, { type: 'module' }) + await new Promise((resolve, reject) => { + peer.onmessage = event => { if (event.data === 'ready') resolve() } + peer.onerror = reject + }) + const hub = new PostMessageHub() + const run = async count => { + let next = 0 + await Promise.all(Array.from({ length: concurrency }, async () => { + while (next < count) { + const value = next++ + if (await hub.emit(peer, 'echo', value) !== value) throw new Error('incorrect result') + } + })) + } + const warmUntil = performance.now() + 1000 + do { await run(1000) } while (performance.now() < warmUntil) + const count = concurrency === 1 ? 5000 : 16000 + const samples = [] + for (let round = 0; round < 2; round++) { + const start = performance.now() + await run(count) + samples.push((performance.now() - start) * 1000 / count) + } + hub.destroy(); peer.terminate(); URL.revokeObjectURL(url) + return samples + }, { origin, version, concurrency }) + aggregate[version].push(...samples) + } finally { await context.close() } + console.error(`worker-steady-${concurrency} ${version} cycle ${cycle + 1}: complete`) + } + } + for (const version of versions) { + const samples = aggregate[version] + const sorted = [...samples].sort((a, b) => a - b) + results.push({ scenario: `worker-steady-${concurrency}`, version, medianUsPerCall: (sorted[2] + sorted[3]) / 2, samplesUsPerCall: samples }) + } + } + console.log(JSON.stringify({ + browser: browser.version(), + methodology: 'Numeric echo; microtask clone transport and native Workers; 8 rotating paused rounds and 3 rotated isolated continuous cycles (2 rounds/cycle) with 1s warmup; concurrency 1 or 32. Concurrent figures are amortized time per call, not individual latency.', + results, + }, null, 2)) +} finally { + await browser?.close() + await new Promise(resolve => server.close(resolve)) +} diff --git a/scripts/check-electron.mjs b/scripts/check-electron.mjs new file mode 100644 index 0000000..f034271 --- /dev/null +++ b/scripts/check-electron.mjs @@ -0,0 +1,51 @@ +import assert from 'node:assert/strict' +import { createRequire } from 'node:module' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' + +const browserRequire = createRequire(new URL('../packages/duplex-message/package.json', import.meta.url)) +const electronRequire = createRequire(process.env.ELECTRON_SMOKE_PACKAGE_DIR + ? resolve(process.env.ELECTRON_SMOKE_PACKAGE_DIR, 'package.json') + : new URL('../packages/simple-electron-ipc/package.json', import.meta.url)) +const { _electron } = browserRequire('playwright') +const args = [fileURLToPath(new URL('../packages/simple-electron-ipc/demo/dist/main.js', import.meta.url))] +// Some container runners cannot create Chromium sandbox namespaces. +if (process.env.ELECTRON_SMOKE_NO_SANDBOX === '1') args.push('--no-sandbox') +const app = await _electron.launch({ executablePath: electronRequire('electron'), args }) +try { + const page = await app.firstWindow() + const errors = [] + page.on('pageerror', error => errors.push(error.message)) + await page.waitForFunction(() => typeof window.duplexDemo?.calculate === 'function') + const preferences = await app.evaluate(({ BrowserWindow }) => { + const prefs = BrowserWindow.getAllWindows()[0].webContents.getLastWebPreferences() + return { nodeIntegration: prefs.nodeIntegration, contextIsolation: prefs.contextIsolation, sandbox: prefs.sandbox } + }) + assert.deepEqual(preferences, { nodeIntegration: false, contextIsolation: true, sandbox: true }) + assert.deepEqual(await page.evaluate(() => ({ require: typeof window.require, process: typeof window.process })), { + require: 'undefined', process: 'undefined', + }) + assert.deepEqual(await page.evaluate(() => Object.keys(window.duplexDemo).sort()), [ + 'calculate', 'cancelDownload', 'download', 'getTitle', + ]) + assert.equal(await page.evaluate(() => window.duplexDemo.calculate(2, 3)), 5) + assert.equal(await page.evaluate(() => window.duplexDemo.getTitle('Title: ')), 'Title: Duplex Message Electron Demo') + const download = await page.evaluate(async () => { + const progress = [] + const result = await window.duplexDemo.download(count => progress.push(count)) + return { result, progress } + }) + assert.equal(download.result, 'done') + assert.deepEqual(download.progress, [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]) + const cancelled = await page.evaluate(async () => { + try { + await window.duplexDemo.download(() => window.duplexDemo.cancelDownload()) + return false + } catch (error) { return String(error.message).includes('aborted') } + }) + assert.equal(cancelled, true) + await page.getByRole('button', { name: 'Calculate 2 + 3 through bidirectional RPC', exact: true }).click() + await page.waitForFunction(() => document.getElementById('get-calc-resp').textContent === '5') + assert.deepEqual(errors, []) + console.log('Electron smoke passed isolated/sandboxed preload configuration, fixed bridge API, typed bidirectional RPC, progress, cancellation and renderer UI.') +} finally { await app.close() } diff --git a/scripts/check-package.mjs b/scripts/check-package.mjs new file mode 100644 index 0000000..2f429a1 --- /dev/null +++ b/scripts/check-package.mjs @@ -0,0 +1,204 @@ +import assert from 'node:assert/strict' +import { execFileSync } from 'node:child_process' +import { cp, mkdir, mkdtemp, readFile, readdir, rm, symlink, writeFile } from 'node:fs/promises' +import { createRequire } from 'node:module' +import { tmpdir } from 'node:os' +import { dirname, join } from 'node:path' +import { fileURLToPath, pathToFileURL } from 'node:url' +import { runInNewContext } from 'node:vm' +import { parse } from 'acorn' + +const root = fileURLToPath(new URL('../', import.meta.url)) +const consumer = await mkdtemp(join(tmpdir(), 'duplex-message-consumer-')) +const packages = ['duplex-message', 'simple-electron-ipc'] +const run = (command, args, cwd = consumer) => execFileSync(command, args, { + cwd, + stdio: 'inherit', + env: { ...process.env, NODE_ENV: 'production' }, +}) + +try { + const archives = join(consumer, 'archives') + await mkdir(archives) + for (const name of packages) { + const source = join(root, 'packages', name) + const manifest = JSON.parse(await readFile(join(source, 'package.json'), 'utf8')) + run('pnpm', ['pack', '--pack-destination', archives], source) + const destination = join(consumer, 'node_modules', name) + await mkdir(destination, { recursive: true }) + run('tar', ['-xzf', join(archives, `${name}-${manifest.version}.tgz`), '--strip-components=1', '-C', destination]) + const packed = JSON.parse(await readFile(join(destination, 'package.json'), 'utf8')) + assert.ok(!JSON.stringify(packed).includes('workspace:'), 'workspace dependency leaked into package') + if (name === 'simple-electron-ipc') assert.match(packed.dependencies['duplex-message'], /^\^\d+\.\d+\.\d+$/) + const verifyEntries = async entries => { + for (const entry of Object.values(entries)) { + if (typeof entry === 'string') await readFile(join(destination, entry)) + else await verifyEntries(entry) + } + } + await verifyEntries(packed.exports['.']) + for (const file of await readdir(join(destination, 'dist'))) { + if (!/\.(?:m?js|cjs)$/.test(file)) continue + parse(await readFile(join(destination, 'dist', file), 'utf8'), { + ecmaVersion: 2018, + sourceType: file.endsWith('.mjs') || file.includes('.es.') ? 'module' : 'script', + }) + } + } + + // Use a transport stub for Node import checks; Electron runtime integration is tested separately. + const electron = join(consumer, 'node_modules', 'electron') + await mkdir(electron) + await writeFile(join(electron, 'package.json'), JSON.stringify({ + name: 'electron', version: '44.5.1', main: 'index.js', types: 'electron.d.ts', + })) + await writeFile(join(electron, 'index.js'), 'module.exports = { ipcMain: { on() {}, off() {} }, ipcRenderer: { on() {}, off() {} } }\n') + const requireElectron = createRequire(join(root, 'packages/simple-electron-ipc/package.json')) + await cp(join(dirname(requireElectron.resolve('electron/package.json')), 'electron.d.ts'), join(electron, 'electron.d.ts')) + await mkdir(join(consumer, 'node_modules', '@types')) + const requireRoot = createRequire(join(root, 'package.json')) + await symlink(dirname(requireRoot.resolve('@types/node/package.json')), join(consumer, 'node_modules', '@types', 'node')) + + const smoke = ` + import assert from 'node:assert/strict' + import { createRequire } from 'node:module' + import * as esm from 'duplex-message' + import { MainMessageHub as EsmMain } from 'simple-electron-ipc' + import * as electronEsm from 'simple-electron-ipc' + const require = createRequire(import.meta.url) + const cjs = require('duplex-message') + assert.equal(esm.PostMessageHub, cjs.PostMessageHub, 'Node import/require loaded different hub classes') + const shared = esm.BroadcastMessageHub.shared + assert.equal(shared, cjs.BroadcastMessageHub.shared, 'Node import/require duplicated shared hubs') + shared.destroy() + for (const entry of ['index.umd', 'index.production.umd']) { + assert.equal(typeof require('duplex-message/dist/' + entry).PostMessageHub, 'function') + } + assert.equal(typeof require('simple-electron-ipc/dist/index').MainMessageHub, 'function') + const { MainMessageHub, RendererMessageHub } = require('simple-electron-ipc') + assert.equal(EsmMain, MainMessageHub, 'Node import/require loaded different Electron classes') + for (const api of [electronEsm, require('simple-electron-ipc')]) { + assert.equal(typeof api.createRpcClient, 'function') + assert.equal(typeof api.waitForPeer, 'function') + assert.equal(typeof api.READY_METHOD, 'string') + assert.equal(api.EErrorCode.REQUEST_TIMEOUT, 7) + } + for (const api of [esm, cjs]) { + assert.equal(typeof api.PostMessageHub, 'function') + const channelName = 'packed-' + Math.random() + const client = new api.BroadcastMessageHub({ channelName }) + const server = new api.BroadcastMessageHub({ channelName }) + try { + server.on('sum', (a, b) => a + b) + server.on(api.READY_METHOD, () => true) + await api.waitForPeer(method => client.emit(method)) + const rpc = api.createRpcClient((method, ...args) => client.emit(method, ...args)) + assert.equal(await rpc.call('sum', 2, 3), 5) + assert.equal(await client.emit(Object.freeze({ methodName: 'sum', to: server.instanceID }), 2, 3), 5) + const controller = new AbortController() + const updates = [] + server.on('progress', (options) => { + options.onprogress('--message-hub-to-be-continued--') + return 'done' + }) + assert.equal(await client.emit({ methodName: 'progress', signal: controller.signal, requestTimeout: 1000 }, { + onprogress(value) { updates.push(value) }, + }), 'done') + assert.deepEqual(updates, ['--message-hub-to-be-continued--']) + server.on('slow', () => new Promise(() => {})) + const cancelled = assert.rejects(client.emit({ methodName: 'slow', signal: controller.signal }), { code: 6 }) + controller.abort() + await cancelled + await assert.rejects(client.emit({ methodName: 'slow', requestTimeout: 20 }), { code: 7 }) + } finally { client.destroy(); server.destroy() } + } + Object.defineProperty(process, 'type', { configurable: true, value: 'browser' }) + new MainMessageHub().destroy() + new EsmMain().destroy() + Object.defineProperty(process, 'type', { configurable: true, value: 'renderer' }) + new RendererMessageHub().destroy() + ` + await writeFile(join(consumer, 'smoke.mjs'), smoke) + run(process.execPath, ['smoke.mjs']) + + const types = ` + import { BroadcastMessageHub, PostMessageHub, EErrorCode, createRpcClient, waitForPeer, type IError } from 'duplex-message' + import { MainMessageHub, RendererMessageHub, createRpcClient as createIpcClient, type IElectronMessageHubOptions } from 'simple-electron-ipc' + const hub = new BroadcastMessageHub() + const result: Promise = hub.emit('sum', 1, 2) + const error: IError = { code: EErrorCode.INVALID_MESSAGE, message: 'test' } + hub.on('sum', (a: number, b: number) => a + b) + const main = new MainMessageHub() + main.on('*', 'sum', (a: number, b: number) => a + b) + const renderer = new RendererMessageHub() + const title: Promise = renderer.emit('title') + const controller = new AbortController() + const timed: Promise = hub.emit({ methodName: 'sum', requestTimeout: 100, signal: controller.signal }, 1, 2) + const ipcTimed: Promise = renderer.emit({ methodName: 'title', requestTimeout: 100, signal: controller.signal }) + const timeoutError: IError = { code: EErrorCode.REQUEST_TIMEOUT, message: 'timeout' } + const abortError: IError = { code: EErrorCode.REQUEST_ABORTED, message: 'aborted' } + interface Api { + add(a: number, b: number): number + title(): Promise + nested(): PromiseLike> + nullable(): number | Promise | null + } + const rpc = createRpcClient((method, ...args) => hub.emit(method, ...args)) + const sum: Promise = rpc.call('add', 1, 2) + const name: Promise = rpc.call({ methodName: 'title', signal: controller.signal }) + const nested: Promise = rpc.call('nested') + const nullable: Promise = rpc.call('nullable') + // @ts-expect-error unknown remote method + rpc.call('missing') + // @ts-expect-error incorrect argument type + rpc.call('add', 'one', 2) + // @ts-expect-error missing required argument + rpc.call('add', 1) + // @ts-expect-error configuration must retain method-specific argument types + rpc.call({ methodName: 'add', requestTimeout: 100 }, 'one', 2) + // @ts-expect-error async return type must be inferred from the remote method + const incorrectResult: Promise = rpc.call('title') + const windowHub = new PostMessageHub({ allowedOrigins: ['https://trusted.example'], targetOrigin: 'https://trusted.example' }) + const checkedMain = new MainMessageHub({ validateSender: event => event.senderFrame?.url === 'file:///app/index.html' }) + const ready: Promise = waitForPeer(method => hub.emit(method), { signal: controller.signal }) + const ipcOptions: IElectronMessageHubOptions = { requestTimeout: 100 } + const ipcClient = createIpcClient((method, ...args) => renderer.emit(method, ...args)) + const ipcSum: Promise = ipcClient.call('add', 1, 2) + // @ts-expect-error configuration must name an existing method + rpc.call({ methodName: 'missing' }) + void [result, error, title, timed, ipcTimed, timeoutError, abortError, sum, name, nested, nullable, incorrectResult, windowHub, checkedMain, ready, ipcOptions, ipcSum] + ` + await writeFile(join(consumer, 'types.cts'), types) + await writeFile(join(consumer, 'types.mts'), types) + await writeFile(join(consumer, 'tsconfig.json'), JSON.stringify({ + compilerOptions: { strict: true, noEmit: true, module: 'NodeNext', moduleResolution: 'NodeNext', target: 'ES2022' }, + include: ['types.cts', 'types.mts'], + })) + run('pnpm', ['exec', 'tsc', '-p', join(consumer, 'tsconfig.json')], root) + + for (const file of ['index.umd.js', 'index.production.umd.js']) { + const context = {} + runInNewContext(await readFile(join(consumer, 'node_modules/duplex-message/dist', file), 'utf8'), context) + assert.equal(typeof context['duplex-message'].PostMessageHub, 'function') + } + for (const name of packages) await readFile(join(consumer, 'node_modules', name, 'dist/index.es.js')) + await readFile(join(consumer, 'node_modules/duplex-message/dist/index.production.es.js')) + + await writeFile(join(consumer, 'shake.mjs'), "export { PageScriptMessageHub } from 'duplex-message'\n") + const requireBrowser = createRequire(join(root, 'packages/duplex-message/package.json')) + const { build } = await import(pathToFileURL(requireBrowser.resolve('vite')).href) + const result = await build({ + configFile: false, + logLevel: 'silent', + build: { write: false, lib: { entry: join(consumer, 'shake.mjs'), formats: ['es'] } }, + }) + const builds = Array.isArray(result) ? result : [result] + const code = builds.flatMap((item) => item.output) + .filter((item) => item.type === 'chunk').map((item) => item.code).join('\n') + for (const unused of ['localStorage', 'BroadcastChannel', '_hostedWorkers', '__duplex_message_ready__']) { + assert.ok(!code.includes(unused), `unused transport retained: ${unused}`) + } + console.log('Packed packages passed CJS/ESM RPC, Electron import, NodeNext types, UMD and tree-shaking checks.') +} finally { + await rm(consumer, { recursive: true, force: true }) +} diff --git a/tsconfig.json b/tsconfig.json index c8f1b3d..3a9a35e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,18 @@ { "compilerOptions": { - "outDir": "./dist/", - "sourceMap": true, - "declaration": true, + "target": "ES2017", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": [ + "ES2017", + "DOM" + ], "strict": true, - "noImplicitReturns": true, - "noImplicitAny": false, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, "noImplicitOverride": true, - "module": "commonjs", - "moduleResolution": "node", "esModuleInterop": true, - "target": "es6", - "allowJs": false, - "baseUrl": "./", - "lib": ["dom", "es7"], - }, - "include": ["./packages/**/*.ts", ".eslintrc.js"] + "skipLibCheck": true, + "types": [ + "node" + ] + } }