From b99da147996541c92b2677f1553625c965d87c65 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 2 Jun 2025 15:01:59 -0500 Subject: [PATCH] feat(runner): Create new `@wxt-dev/runner` package (#1566) --- .github/workflows/release.yml | 1 + .github/workflows/sync-releases.yml | 1 + .gitignore | 1 + docs/.vitepress/config.ts | 2 + docs/runner.md | 1 + packages/runner/README.md | 225 +++++ packages/runner/demo-extension/background.js | 1 + packages/runner/demo-extension/manifest.json | 9 + packages/runner/dev.ts | 16 + packages/runner/package.json | 51 ++ packages/runner/src/__tests__/install.test.ts | 80 ++ packages/runner/src/__tests__/options.test.ts | 306 +++++++ packages/runner/src/bidi.ts | 75 ++ packages/runner/src/browser-paths.ts | 120 +++ packages/runner/src/cdp.ts | 57 ++ packages/runner/src/debug.ts | 24 + packages/runner/src/index.ts | 3 + packages/runner/src/install.ts | 59 ++ packages/runner/src/options.ts | 286 ++++++ packages/runner/src/promises.ts | 19 + packages/runner/src/run.ts | 125 +++ packages/runner/src/web-socket.ts | 37 + packages/runner/tsconfig.json | 4 + packages/runner/vitest.config.ts | 8 + pnpm-lock.yaml | 855 ++++++++++-------- 25 files changed, 2000 insertions(+), 366 deletions(-) create mode 100644 docs/runner.md create mode 100644 packages/runner/README.md create mode 100644 packages/runner/demo-extension/background.js create mode 100644 packages/runner/demo-extension/manifest.json create mode 100644 packages/runner/dev.ts create mode 100644 packages/runner/package.json create mode 100644 packages/runner/src/__tests__/install.test.ts create mode 100644 packages/runner/src/__tests__/options.test.ts create mode 100644 packages/runner/src/bidi.ts create mode 100644 packages/runner/src/browser-paths.ts create mode 100644 packages/runner/src/cdp.ts create mode 100644 packages/runner/src/debug.ts create mode 100644 packages/runner/src/index.ts create mode 100644 packages/runner/src/install.ts create mode 100644 packages/runner/src/options.ts create mode 100644 packages/runner/src/promises.ts create mode 100644 packages/runner/src/run.ts create mode 100644 packages/runner/src/web-socket.ts create mode 100644 packages/runner/tsconfig.json create mode 100644 packages/runner/vitest.config.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c4fb5680..6fddf9dd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,6 +14,7 @@ on: - module-solid - module-svelte - module-vue + - runner - storage - unocss - webextension-polyfill diff --git a/.github/workflows/sync-releases.yml b/.github/workflows/sync-releases.yml index 7e6ddca0..2ecfe580 100644 --- a/.github/workflows/sync-releases.yml +++ b/.github/workflows/sync-releases.yml @@ -14,6 +14,7 @@ on: - module-solid - module-svelte - module-vue + - runner - storage - webextension-polyfill - wxt diff --git a/.gitignore b/.gitignore index c4984e4c..c395729e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ .output .webextrc .wxt +.wxt-runner *.log /docs/.vitepress/cache docs/.vitepress/.temp diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 515aa054..b0de0d30 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -15,6 +15,7 @@ import { version as autoIconsVersion } from '../../packages/auto-icons/package.j import { version as unocssVersion } from '../../packages/unocss/package.json'; import { version as storageVersion } from '../../packages/storage/package.json'; import { version as analyticsVersion } from '../../packages/analytics/package.json'; +import { version as runnerVersion } from '../../packages/runner/package.json'; import addKnowledge from 'vitepress-knowledge'; import { groupIconMdPlugin, @@ -41,6 +42,7 @@ const otherPackages = { i18n: i18nVersion, storage: storageVersion, unocss: unocssVersion, + runner: runnerVersion, }; const knowledge = addKnowledge({ diff --git a/docs/runner.md b/docs/runner.md new file mode 100644 index 00000000..f3e8b4c3 --- /dev/null +++ b/docs/runner.md @@ -0,0 +1 @@ + diff --git a/packages/runner/README.md b/packages/runner/README.md new file mode 100644 index 00000000..3bfc2144 --- /dev/null +++ b/packages/runner/README.md @@ -0,0 +1,225 @@ +# `@wxt-dev/runner` + +Programmatically open a browser and install a web extension from a local directory. + +###### With WXT + +> [!WARNING] +> This package is intended to replace [`web-ext`](https://github.com/mozilla/web-ext) in the future, but it is not ready at the moment. Once it's ready for testing in WXT, more details will be added here. + +```ts +// ~/wxt.runner.config.ts OR /wxt.runner.config.ts +import { defineRunnerConfig } from 'wxt'; + +export default defineRunnerConfig({ + // Options go here +}); +``` + +###### JS API + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + extensionDir: '/path/to/extension', + // Other options... +}); +``` + +## Features + +- Supports all Chromium and Firefox based browsers +- Zero dependencies +- One-line config for persisting data between launches + +## Requirements + +`@wxt-dev/runner` requires a JS runtime that implements the `WebSocket` standard: + +| JS Runtime | Version | +| ---------- | ----------- | +| NodeJS | ≥ 22.4.0 | +| Bun | ≥ 1.2.0 | + +You also need to have a specific version of the browser installed that supports the latest features so extensions can be loaded: + +| Browser | Version | +| -------- | -------- | +| Chromium | Unknown | +| Firefox | ≥ 139 | + +## TODO + +- [x] Provide install functions to allow hooking into already running instances of Chrome/Firefox + - [ ] Try to setup E2E tests on Firefox with Puppeteer using this approach + - [ ] Try to setup E2E tests on Chrome with Puppeteer using this approach + +## Options + +### Target + +To open a specific browser, use the `target` option: + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + extensionDir: 'path/to/extension', + target: 'firefox', +}); +``` + +Defaults to opening `chrome`. You may see type-hints for a list of popular browsers, but you can enter any string you want here. + +### Data Persistence + +Browsers block you from using your normal browser profiles when using the [BiDi and CDP protocols](#implementation-details) for security reasons. + +To change how the new profile's data is saved between sessions, use the `dataPersistence` option: + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + dataPersistence: 'user', +}); +``` + +- `"none"` (default): Use a brand new browser profile every time the browser is opened (stored in the system's tmp directory) +- `"project"`: Create a new profile that is re-used for your current directory (by default stored in `.wxt-runner` or `.wxt/runner` for WXT projects) +- `"user"`: Create a new profile that is re-used for all projects using `@wxt-dev/runner` (by default stored in `$HOME/.wxt-runner`) + +These presets configure different flags for different operating systems when spawning the browser process. + +If you want to customize your data persistence beyond what these presets define, [you can override the browser flags yourself](#Arguments) to configure persistence. + +### Browser Binaries + +`@wxt-dev/runner` will look for browser binaries/executables in [a hard-coded list of paths](https://github.com/wxt-dev/wxt/blob/main/packages/runner/src/browser-paths.ts). It does not and will not explore your filesystem/`$PATH` to find where the browser is installed. That means there are times you will need to specify the path to a browser's binary on your system: + +- Your browser's path is non-standard or missing from the hard-coded list. +- You want to use a specific version/release of the browser. +- You're using a less popular browser and `@wxt-dev/runner` doesn't have hard-coded paths for it. + +To do this, use the `browserBinaries` option and set the path to the browser's binary: + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + extensionDir: 'path/to/extension', + browserBinaries: { + chrome: '/path/to/chrome', + firefox: '/path/to/firefox', + }, +}); +``` + +### Arguments + +To pass custom arguments to the browser on startup, use the `chromiumArgs` or `firefoxArgs` options: + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + extensionDir: 'path/to/extension', + chromiumArgs: ['--window-size=1920,1080'], + firefoxArgs: ['--window-size', '1920,1080'], +}); +``` + +### Start URLs + +To open specific URLs in tabs by default, you also use the `chromiumArgs` or `firefoxArgs` options. + +Any URLs passed as a CLI argument will be opened in the browser when it starts. + +```ts +import { run } from '@wxt-dev/runner'; + +await run({ + extensionDir: 'path/to/extension', + chromiumArgs: ['https://example.com'], + firefoxArgs: ['https://example.com'], +}); +``` + +### Debugging + +To see debug logs, set the `DEBUG` env var to `"@wxt-dev/runner"`. This will print the resolved config, commands used to spawn the browser, any messages sent on the browser's communication protocol, and more for you to debug. + +
+Example debug output + +``` +@wxt-dev/runner:options User options: { extensionDir: 'demo-extension', target: undefined } +@wxt-dev/runner:options Resolved options: { + browserBinary: '/usr/bin/chromium', + chromiumArgs: [ + '--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculateNativeWinOcclusion,InterestFeedContentSuggestions,CertificateTransparencyComponentUpdater,AutofillServerCommunication,PrivacySandboxSettings4', + '--disable-component-extensions-with-background-pages', + '--disable-background-networking', + '--disable-component-update', + '--disable-client-side-phishing-detection', + '--disable-sync', + '--metrics-recording-only', + '--disable-default-apps', + '--no-default-browser-check', + '--no-first-run', + '--disable-background-timer-throttling', + '--disable-ipc-flooding-protection', + '--password-store=basic', + '--use-mock-keychain', + '--force-fieldtrials=*BackgroundTracing/default/', + '--disable-hang-monitor', + '--disable-prompt-on-repost', + '--disable-domain-reliability', + '--propagate-iph-for-testing', + '--remote-debugging-port=0', + '--remote-debugging-pipe', + '--user-data-dir=/tmp/wxt-runner-pWXLO1', + '--enable-unsafe-extension-debugging' + ], + dataDir: '/tmp/wxt-runner-pWXLO1', + dataPersistence: 'none', + chromiumRemoteDebuggingPort: 0, + extensionDir: '/home/aklinker1/Development/github.com/wxt-dev/wxt/packages/runner/demo-extension', + firefoxArgs: [ + '--new-instance', + '--no-remote', + '--profile', + '/tmp/wxt-runner-pWXLO1', + '--remote-debugging-port=0', + 'about:debugging#/runtime/this-firefox' + ], + firefoxRemoteDebuggingPort: 0, + target: 'chrome' +} +@wxt-dev/runner:chrome:stderr DevTools listening on ws://127.0.0.1:38397/devtools/browser/93dc4de5-64cb-4e0b-a9d3-7549527015f0 +@wxt-dev/runner:cdp Sending command: { + id: 1, + method: 'Extensions.loadUnpacked', + params: { + path: '/home/aklinker1/Development/github.com/wxt-dev/wxt/packages/runner/demo-extension' + } +} +@wxt-dev/runner:cdp Received response: { id: 1, result: { id: 'hckhakegfgenefhikdcfkaaonnclljmf' } } +``` + +
+ +## Implementation Details + +All this package does is spawn a child process to open the browser with some default flags before using remote protocols to install the extension. + +### Firefox + +We use the new [WebDriver BiDi protocol](https://www.w3.org/TR/webdriver-bidi) to install the extension. This just involves connecting to a web socket and sending a few messages. + +### Chrome + +We use the [CDP](https://chromedevtools.github.io/devtools-protocol/) with `--remote-debugging-pipe` and `--enable-unsafe-extension-debugging` to install the extension by sending a message via IO pipes 3 and 4. + +We don't use Webdriver Bidi because it's not built into Chrome yet. It requires us instantiating a separate child process for `chromedriver`. This is slower and more difficult than just using the CDP built into Chrome. diff --git a/packages/runner/demo-extension/background.js b/packages/runner/demo-extension/background.js new file mode 100644 index 00000000..410ef831 --- /dev/null +++ b/packages/runner/demo-extension/background.js @@ -0,0 +1 @@ +console.log('Hello background!'); diff --git a/packages/runner/demo-extension/manifest.json b/packages/runner/demo-extension/manifest.json new file mode 100644 index 00000000..d1cae612 --- /dev/null +++ b/packages/runner/demo-extension/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Test", + "version": "1.0.0", + "manifest_version": 3, + "background": { + "service_worker": "background.js", + "scripts": ["background.js"] + } +} diff --git a/packages/runner/dev.ts b/packages/runner/dev.ts new file mode 100644 index 00000000..dcf09a3f --- /dev/null +++ b/packages/runner/dev.ts @@ -0,0 +1,16 @@ +// +// USAGE: +// pnpm dev +// pnpm dev firefox-nightly +// pnpm dev +// + +import { run } from './src'; + +// Uncomment to enable debug logs +process.env.DEBUG = '@wxt-dev/runner'; + +await run({ + extensionDir: 'demo-extension', + target: process.argv[2], +}); diff --git a/packages/runner/package.json b/packages/runner/package.json new file mode 100644 index 00000000..10edf6ec --- /dev/null +++ b/packages/runner/package.json @@ -0,0 +1,51 @@ +{ + "name": "@wxt-dev/runner", + "description": "Launch Chrome and Firefox with a web extension installed", + "version": "0.1.0", + "type": "module", + "repository": { + "type": "git", + "url": "git+https://github.com/wxt-dev/wxt.git", + "directory": "packages/runner" + }, + "homepage": "https://github.com/wxt-dev/wxt/tree/main/packages/runner#readme", + "keywords": [ + "web-extension", + "chrome-extension", + "wxt" + ], + "author": { + "name": "Aaron Klinker", + "email": "aaronklinker1+wxt@gmail.com" + }, + "license": "MIT", + "funding": "https://github.com/sponsors/wxt-dev", + "scripts": { + "check": "pnpm build && check", + "test": "buildc --deps-only -- vitest", + "dev": "tsx --trace-warnings dev.ts", + "build": "buildc -- unbuild", + "prepublishOnly": "pnpm build" + }, + "dependencies": {}, + "devDependencies": { + "@aklinker1/check": "2.0.0", + "oxlint": "^0.16.8", + "publint": "^0.3.12", + "tsx": "4.19.4", + "typescript": "^5.8.3", + "unbuild": "^3.5.0", + "vitest": "^3.1.2" + }, + "main": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + } + }, + "files": [ + "dist" + ] +} diff --git a/packages/runner/src/__tests__/install.test.ts b/packages/runner/src/__tests__/install.test.ts new file mode 100644 index 00000000..4ac12d9b --- /dev/null +++ b/packages/runner/src/__tests__/install.test.ts @@ -0,0 +1,80 @@ +import { describe, it, expect, vi } from 'vitest'; +import { mock } from 'vitest-mock-extended'; +import { createCdpConnection, type CDPConnection } from '../cdp'; +import { createBidiConnection, type BidiConnection } from '../bidi'; +import type { ChildProcess } from 'node:child_process'; +import { installChromium, installFirefox } from '../install'; + +vi.mock('../cdp'); +const createCdpConnectionMock = vi.mocked(createCdpConnection); + +vi.mock('../bidi'); +const createBidiConnectionMock = vi.mocked(createBidiConnection); + +describe('Install', () => { + describe('Chromium', () => { + it('Should send the install command to the process', async () => { + const browserProcess = mock(); + const connection = mock({ + [Symbol.dispose]: vi.fn(), + }); + const extensionDir = '/path/to/extension'; + const expectedExtensionId = 'chromium-extension-id'; + + createCdpConnectionMock.mockReturnValue(connection); + connection.send.mockImplementation(async (method) => { + if (method === 'Extensions.loadUnpacked') + return { id: expectedExtensionId }; + throw Error('Unknown method'); + }); + + const res = await installChromium(browserProcess, extensionDir); + + expect(createCdpConnectionMock).toBeCalledTimes(1); + expect(createCdpConnectionMock).toBeCalledWith(browserProcess); + + expect(connection.send).toBeCalledTimes(1); + expect(connection.send).toBeCalledWith('Extensions.loadUnpacked', { + path: extensionDir, + }); + + expect(res).toEqual({ id: expectedExtensionId }); + }); + }); + + describe('Firefox', () => { + it('Should connect to the server, start a session, then install the extension', async () => { + const debuggerUrl = 'http://127.0.0.1:9222'; + const extensionDir = '/path/to/extension'; + const expectedExtensionId = 'firefox-extension-id'; + const connection = mock({ + [Symbol.dispose]: vi.fn(), + }); + + createBidiConnectionMock.mockResolvedValue(connection); + connection.send.mockImplementation(async (method) => { + if (method === 'session.new') return { sessionId: 'session-id' }; + if (method === 'webExtension.install') + return { extension: expectedExtensionId }; + }); + + const res = await installFirefox(debuggerUrl, extensionDir); + + expect(createBidiConnectionMock).toBeCalledTimes(1); + expect(createBidiConnectionMock).toBeCalledWith(debuggerUrl); + + expect(connection.send).toBeCalledTimes(2); + expect(connection.send).toBeCalledWith('session.new', { + capabilities: {}, + }); + expect(connection.send).toBeCalledWith('webExtension.install', { + extensionData: { + type: 'path', + path: extensionDir, + }, + }); + + expect(res).toEqual({ extension: expectedExtensionId }); + }); + }); +}); diff --git a/packages/runner/src/__tests__/options.test.ts b/packages/runner/src/__tests__/options.test.ts new file mode 100644 index 00000000..23ec3e4a --- /dev/null +++ b/packages/runner/src/__tests__/options.test.ts @@ -0,0 +1,306 @@ +import { describe, it, expect, vi, beforeAll } from 'vitest'; +import { ResolvedRunOptions, resolveRunOptions } from '../options'; +import { resolve, join } from 'node:path'; +import { tmpdir, homedir } from 'node:os'; +import { mkdir } from 'node:fs/promises'; + +vi.mock('node:os', async () => { + const { vi } = await import('vitest'); + const os: any = await vi.importActual('node:os'); + const { join } = await import('node:path'); + return { + ...os, + tmpdir: () => join(os.tmpdir(), 'tmpdir-mock'), + homedir: () => join(os.tmpdir(), 'homedir-mock'), + }; +}); + +describe('Options', () => { + beforeAll(async () => { + // Make sure mock test directories exist + await mkdir(tmpdir(), { recursive: true }); + await mkdir(homedir(), { recursive: true }); + }); + + describe('extensionDir', () => { + it('should default to the current working directory', async () => { + const actual = await resolveRunOptions({}); + expect(actual).toMatchObject>({ + extensionDir: process.cwd(), + }); + }); + + it('should resolve relative to the current working directory', async () => { + const actual = await resolveRunOptions({ + extensionDir: './path/to/extension', + }); + expect(actual).toMatchObject>({ + extensionDir: resolve(process.cwd(), './path/to/extension'), + }); + }); + }); + + describe('target', () => { + it('should be "chrome" by default', async () => { + const actual = await resolveRunOptions({ + extensionDir: 'path/to/extension', + }); + expect(actual).toMatchObject>({ + target: 'chrome', + }); + }); + + it('should be what is passed in', async () => { + const actual = await resolveRunOptions({ + extensionDir: 'path/to/extension', + target: 'custom', + browserBinaries: { + custom: '/path/to/custom/browser', + }, + }); + expect(actual).toMatchObject>({ + target: 'custom', + }); + }); + + it('should throw an error if the target binary could not be found', async () => { + const actual = resolveRunOptions({ + extensionDir: 'path/to/extension', + target: 'custom', + }); + await expect(actual).rejects.toThrow('Could not find "custom" binary.'); + }); + }); + + describe('browserBinary', () => { + it('should denormalize the browserBinary', async () => { + const path = + process.platform === 'win32' + ? 'C:/path/to/custom/browser.exe' + : '/path/to/custom/browser'; + const expectedPath = + process.platform === 'win32' + ? 'C:\\path\\to\\custom\\browser.exe' + : path; + const actual = await resolveRunOptions({ + extensionDir: 'path/to/extension', + target: 'custom', + browserBinaries: { + custom: path, + }, + }); + expect(actual).toMatchObject>({ + browserBinary: expectedPath, + }); + }); + }); + + describe('chromiumArgs', () => { + it('should log a warning when --user-data-dir is passed in', async () => { + const warnSpy = vi.spyOn(console, 'warn'); + await resolveRunOptions({ + chromiumArgs: ['--user-data-dir=some/custom/path'], + }); + expect(warnSpy).toBeCalledTimes(1); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Custom Chromium --user-data-dir argument ignored', + ), + ); + }); + + it('should log a warning when --remote-debugging-port is passed in', async () => { + const warnSpy = vi.spyOn(console, 'warn'); + await resolveRunOptions({ + chromiumArgs: ['--remote-debugging-port=9222'], + }); + expect(warnSpy).toBeCalledTimes(1); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Custom Chromium --remote-debugging-port argument ignored', + ), + ); + }); + + it('should combine default args with user provided ones', async () => { + const actual = await resolveRunOptions({ + chromiumArgs: ['--window-size=1920,1080'], + }); + expect(actual.chromiumArgs).toEqual([ + // Defaults + '--disable-features=Translate,OptimizationHints,MediaRouter,DialMediaRouteProvider,CalculateNativeWinOcclusion,InterestFeedContentSuggestions,CertificateTransparencyComponentUpdater,AutofillServerCommunication,PrivacySandboxSettings4', + '--disable-component-extensions-with-background-pages', + '--disable-background-networking', + '--disable-component-update', + '--disable-client-side-phishing-detection', + '--disable-sync', + '--metrics-recording-only', + '--disable-default-apps', + '--no-default-browser-check', + '--no-first-run', + '--disable-background-timer-throttling', + '--disable-ipc-flooding-protection', + '--password-store=basic', + '--use-mock-keychain', + '--force-fieldtrials=*BackgroundTracing/default/', + '--disable-hang-monitor', + '--disable-prompt-on-repost', + '--disable-domain-reliability', + '--propagate-iph-for-testing', + // Debugging + expect.stringContaining('--remote-debugging-port='), + '--remote-debugging-pipe', + expect.stringContaining('--user-data-dir='), // See dataPersistence tests + '--enable-unsafe-extension-debugging', + // User provided + '--window-size=1920,1080', + ]); + }); + }); + + describe('firefoxArgs', () => { + it('should log a warning when --profile is passed in', async () => { + const warnSpy = vi.spyOn(console, 'warn'); + await resolveRunOptions({ + firefoxArgs: ['--profile=some/custom/path'], + }); + expect(warnSpy).toBeCalledTimes(1); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining('Custom Firefox --profile argument ignored'), + ); + }); + + it('should log a warning when --remote-debugging-port is passed in', async () => { + const warnSpy = vi.spyOn(console, 'warn'); + await resolveRunOptions({ + firefoxArgs: ['--remote-debugging-port=9222'], + }); + expect(warnSpy).toBeCalledTimes(1); + expect(warnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'Custom Firefox --remote-debugging-port argument ignored', + ), + ); + }); + + it('should combine default args with user provided ones', async () => { + const actual = await resolveRunOptions({ + firefoxArgs: ['--window-size=1920,1080'], + }); + expect(actual.firefoxArgs).toEqual([ + // Defaults + '--new-instance', + '--no-remote', + '--profile', + expect.any(String), // See dataPersistence tests + expect.stringContaining('--remote-debugging-port='), + 'about:debugging#/runtime/this-firefox', + // User provided + '--window-size=1920,1080', + ]); + }); + }); + + describe('chromiumRemoteDebuggingPort', () => { + it('should default to 0', async () => { + const actual = await resolveRunOptions({}); + expect(actual).toMatchObject>({ + chromiumRemoteDebuggingPort: 0, + chromiumArgs: expect.arrayContaining([`--remote-debugging-port=0`]), + }); + }); + + it('should respect user provided port', async () => { + const actual = await resolveRunOptions({ + chromiumRemoteDebuggingPort: 9222, + }); + expect(actual).toMatchObject>({ + chromiumRemoteDebuggingPort: 9222, + chromiumArgs: expect.arrayContaining([`--remote-debugging-port=9222`]), + }); + }); + }); + + describe('firefoxRemoteDebuggingPort', () => { + it('should default to 0', async () => { + const actual = await resolveRunOptions({}); + expect(actual).toMatchObject>({ + firefoxRemoteDebuggingPort: 0, + firefoxArgs: expect.arrayContaining([`--remote-debugging-port=0`]), + }); + }); + + it('should respect user provided port', async () => { + const actual = await resolveRunOptions({ + firefoxRemoteDebuggingPort: 9222, + }); + expect(actual).toMatchObject>({ + firefoxRemoteDebuggingPort: 9222, + firefoxArgs: expect.arrayContaining([`--remote-debugging-port=9222`]), + }); + }); + }); + + describe('dataPersistence', () => { + it('should default to "none"', async () => { + const actual = await resolveRunOptions({}); + expect(actual).toMatchObject>({ + dataPersistence: 'none', + }); + }); + + it('should use a temporary directory when set to "none"', async () => { + const actual = await resolveRunOptions({ + dataPersistence: 'none', + }); + expect(actual).toMatchObject>({ + dataPersistence: 'none', + dataDir: expect.stringContaining(join(tmpdir(), 'wxt-runner-')), + chromiumArgs: expect.arrayContaining([ + expect.stringContaining( + `--user-data-dir=${join(tmpdir(), 'wxt-runner-')}`, + ), + ]), + firefoxArgs: expect.arrayContaining([ + expect.stringContaining(join(tmpdir(), 'wxt-runner-')), + ]), + }); + }); + + it('should use a directory in the current working directory when set to "project"', async () => { + const actual = await resolveRunOptions({ + dataPersistence: 'project', + }); + expect(actual).toMatchObject>({ + dataPersistence: 'project', + dataDir: expect.stringContaining(join(process.cwd(), '.wxt-runner')), + chromiumArgs: expect.arrayContaining([ + expect.stringContaining( + `--user-data-dir=${join(process.cwd(), '.wxt-runner')}`, + ), + ]), + firefoxArgs: expect.arrayContaining([ + expect.stringContaining(join(process.cwd(), '.wxt-runner')), + ]), + }); + }); + + it('should use a directory in the user\'s home directory when set to "user"', async () => { + const actual = await resolveRunOptions({ + dataPersistence: 'user', + }); + expect(actual).toMatchObject>({ + dataPersistence: 'user', + dataDir: expect.stringContaining(join(homedir(), '.wxt-runner')), + chromiumArgs: expect.arrayContaining([ + expect.stringContaining( + `--user-data-dir=${join(homedir(), '.wxt-runner')}`, + ), + ]), + firefoxArgs: expect.arrayContaining([ + expect.stringContaining(join(homedir(), '.wxt-runner')), + ]), + }); + }); + }); +}); diff --git a/packages/runner/src/bidi.ts b/packages/runner/src/bidi.ts new file mode 100644 index 00000000..d784d355 --- /dev/null +++ b/packages/runner/src/bidi.ts @@ -0,0 +1,75 @@ +import { openWebSocket } from './web-socket'; +import { debug } from './debug'; + +const debugBidi = debug.scoped('bidi'); + +export interface BidiConnection extends Disposable { + send(method: string, params: any, timeout?: number): Promise; + close(): void; +} + +export async function createBidiConnection( + baseUrl: string, +): Promise { + const url = new URL('/session', baseUrl); + debugBidi('Connecting to BiDi server @', url.href); + + const webSocket = await openWebSocket(url.href); + debugBidi('Connected'); + + let requestId = 0; + + return { + send(method, params, timeout = 10e3) { + const id = ++requestId; + const command = { id, method, params }; + debugBidi('Sending command:', command); + + return new Promise((resolve, reject) => { + const cleanup = () => { + webSocket.removeEventListener('message', onMessage); + webSocket.removeEventListener('error', onError); + }; + + setTimeout(() => { + cleanup(); + reject( + new Error( + `Timed out after ${timeout}ms waiting for ${method} response`, + ), + ); + }, timeout); + + const onMessage = (event: MessageEvent) => { + const data = JSON.parse(event.data); + if (data.id === id) { + debugBidi('Received response:', data); + cleanup(); + if (data.type === 'success') resolve(data.result); + else reject(Error(data.message, { cause: data })); + } + }; + const onError = (error: any) => { + cleanup(); + reject(new Error('Error sending request', { cause: error })); + }; + + webSocket.addEventListener('message', onMessage); + webSocket.addEventListener('error', onError); + + webSocket.send(JSON.stringify(command)); + }); + }, + + close() { + debugBidi('Closing connection...'); + webSocket.close(); + debugBidi('Closed connection'); + }, + [Symbol.dispose]() { + debugBidi('Disposing connection...'); + webSocket.close(); + debugBidi('Disposed connection'); + }, + }; +} diff --git a/packages/runner/src/browser-paths.ts b/packages/runner/src/browser-paths.ts new file mode 100644 index 00000000..b2d2a6ba --- /dev/null +++ b/packages/runner/src/browser-paths.ts @@ -0,0 +1,120 @@ +export type BrowserPlatform = 'windows' | 'mac' | 'linux'; + +export type KnownTarget = + | 'chromium' + | 'chrome' + | 'chrome-beta' + | 'chrome-dev' + | 'chrome-canary' + | 'edge' + | 'edge-beta' + | 'edge-dev' + | 'edge-canary' + | 'firefox' + | 'firefox-nightly' + | 'firefox-developer-edition' + | 'zen'; + +export const KNOWN_BROWSER_PATHS: Record< + KnownTarget, + Record +> = { + // Chromium based targets + + chromium: { + mac: [], + linux: [ + // Arch + '/usr/bin/chromium', + ], + windows: [], + }, + chrome: { + mac: ['/Applications/Chrome.app/Contents/MacOS/Google Chrome'], + linux: [], + windows: ['C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'], + }, + 'chrome-beta': { + mac: [], + linux: [], + windows: [], + }, + 'chrome-canary': { + mac: [ + '/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary', + ], + linux: [], + windows: [], + }, + 'chrome-dev': { + mac: [], + linux: [], + windows: [], + }, + edge: { + mac: [], + linux: [], + windows: [], + }, + 'edge-beta': { + mac: [], + linux: [], + windows: [], + }, + 'edge-canary': { + mac: [], + linux: [], + windows: [], + }, + 'edge-dev': { + mac: [], + linux: [], + windows: [], + }, + + // Firefox based targets + + firefox: { + mac: ['/Applications/Firefox.app/Contents/MacOS/firefox'], + linux: [ + // Arch + '/usr/bin/firefox', + ], + windows: [], + }, + 'firefox-nightly': { + mac: ['/Applications/Firefox Nightly.app/Contents/MacOS/firefox'], + linux: [], + windows: ['C:\\Program Files\\Firefox Nightly\\firefox.exe'], + }, + 'firefox-developer-edition': { + mac: ['/Applications/Firefox Developer Edition.app/Contents/MacOS/firefox'], + linux: [ + // Arch + '/usr/bin/firefox-developer-edition', + ], + windows: [], + }, + zen: { + mac: ['/Applications/Zen Browser.app/Contents/MacOS/zen'], + linux: [], + windows: [], + }, +}; + +/** + * When targeting a browser, this map contains the other targets to fall back on when a binary could not be found for the primary target. + */ +export const FALLBACK_TARGETS: Partial> = { + chrome: [ + 'chromium', + 'chrome-canary', + 'chrome-beta', + 'chrome-dev', + 'edge', + 'edge-canary', + 'edge-beta', + 'edge-dev', + ], + firefox: ['firefox-developer-edition', 'firefox-nightly', 'zen'], +}; diff --git a/packages/runner/src/cdp.ts b/packages/runner/src/cdp.ts new file mode 100644 index 00000000..b96ac3ca --- /dev/null +++ b/packages/runner/src/cdp.ts @@ -0,0 +1,57 @@ +import type { ChildProcess } from 'node:child_process'; +import type { Readable, Writable } from 'node:stream'; +import { debug } from './debug'; + +const debugCdp = debug.scoped('cdp'); + +export interface CDPConnection extends Disposable { + send(method: string, params: any, timeout?: number): Promise; + close(): void; +} + +export function createCdpConnection( + browserProcess: ChildProcess, +): CDPConnection { + const inputStream = browserProcess.stdio[3] as Writable; + const outputStream = browserProcess.stdio[4] as Readable; + + let requestId = 0; + + return { + send(method, params, timeout = 10e3) { + const id = ++requestId; + const command = { id, method, params }; + debugCdp('Sending command:', command); + + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error(`CDP command timed out: ${method}`)); + }, timeout); + + const onData = (data: Buffer) => { + // Trim the trailing null character + const text = data.toString().slice(0, -1); + const res = JSON.parse(text); + + if (res.id !== id) return; + + debugCdp('Received response:', res); + clearTimeout(timer); + outputStream.removeListener('data', onData); + + if ('error' in res) { + reject(new Error(res.error.message, { cause: res.error })); + } else { + resolve(res.result); + } + }; + + outputStream.addListener('data', onData); + + inputStream.write(JSON.stringify(command) + '\0'); + }); + }, + close() {}, + [Symbol.dispose]() {}, + }; +} diff --git a/packages/runner/src/debug.ts b/packages/runner/src/debug.ts new file mode 100644 index 00000000..fbf91e39 --- /dev/null +++ b/packages/runner/src/debug.ts @@ -0,0 +1,24 @@ +export interface Debug { + (...args: any[]): void; + scoped: (scope: string) => Debug; +} + +function createDebug(scopes: string[]): Debug { + const debug = (...args: any[]) => { + const scope = scopes.join(':'); + if ( + process.env.DEBUG === '1' || + process.env.DEBUG === 'true' || + scope.startsWith(process.env.DEBUG ?? '@NOT') + ) { + const params = scope ? [`\x1b[31m${scope}\x1b[0m`, ...args] : args; + console.log(...params); + } + }; + + debug.scoped = (scope: string) => createDebug([...scopes, scope]); + + return debug; +} + +export const debug = createDebug(['@wxt-dev/runner']); diff --git a/packages/runner/src/index.ts b/packages/runner/src/index.ts new file mode 100644 index 00000000..98b4a0dc --- /dev/null +++ b/packages/runner/src/index.ts @@ -0,0 +1,3 @@ +export * from './run'; +export * from './options'; +export * from './install'; diff --git a/packages/runner/src/install.ts b/packages/runner/src/install.ts new file mode 100644 index 00000000..2338f22a --- /dev/null +++ b/packages/runner/src/install.ts @@ -0,0 +1,59 @@ +import type { ChildProcess } from 'node:child_process'; +import { createBidiConnection } from './bidi'; +import { createCdpConnection } from './cdp'; + +/** + * Install an extension to an already running instance of Firefox. + * @param debuggerUrl The URL of the Firefox BiDi server (ex: `ws://127.0.0.1:45912`). + * @param extensionDir Absolute path to the directory containing the extension to be installed. + */ +export async function installFirefox( + debuggerUrl: string, + extensionDir: string, +): Promise { + using bidi = await createBidiConnection(debuggerUrl); + + // Start a session + await bidi.send('session.new', { capabilities: {} }); + + // Install the extension + return await bidi.send( + 'webExtension.install', + { + extensionData: { + type: 'path', + path: extensionDir, + }, + }, + ); +} + +export type BidiWebExtensionInstallResponse = { + extension: string; +}; + +/** + * Given a child process of Chrome, install an extension. The process must be started with the following flags: + * + * - `--remote-debugging-pipe` + * - `--user-data-dir=...` + * - `--enable-unsafe-extension-debugging` + * + * Otherwise it the CDP doesn't have permission to install extensions. + */ +export async function installChromium( + browserProcess: ChildProcess, + extensionDir: string, +): Promise { + using cdp = createCdpConnection(browserProcess); + return await cdp.send( + 'Extensions.loadUnpacked', + { + path: extensionDir, + }, + ); +} + +export type CdpExtensionsLoadUnpackedResponse = { + id: string; +}; diff --git a/packages/runner/src/options.ts b/packages/runner/src/options.ts new file mode 100644 index 00000000..9aab2bf5 --- /dev/null +++ b/packages/runner/src/options.ts @@ -0,0 +1,286 @@ +import { + FALLBACK_TARGETS, + KNOWN_BROWSER_PATHS, + KnownTarget, + type BrowserPlatform, +} from './browser-paths'; +import { resolve, join } from 'node:path'; +import { homedir, tmpdir } from 'node:os'; +import { debug } from './debug'; +import { mkdtemp, open } from 'node:fs/promises'; + +const debugOptions = debug.scoped('options'); + +export type UnknownTarget = string & {}; +export type Target = KnownTarget | UnknownTarget; + +export type RunOptions = { + /** Paths to binaries to use for each target. */ + browserBinaries?: Record; + /** Customize the arguments passed to the chromium binary. Conflicting arguments with required ones to install extensions are ignored. */ + chromiumArgs?: string[]; + /** Control how data is persisted between launches. Either save data at a user level, project level, or don't persist data at all. Defaults to `project`. */ + dataPersistence?: 'user' | 'project' | 'none'; + /** Customize where your profile's data is stored when using `dataPersistence: 'project'`. Can be absolute or relative to the current working directory. */ + projectDataDir?: string; + /** Customize the port Chrome's debugger is listening on. Defaults to a random open port. */ + chromiumRemoteDebuggingPort?: number; + /** Directory where the extension will be installed from. Should contain a `manifest.json` file. Can be relative to the current working directory. Defaults to the current working directory. */ + extensionDir?: string; + /** Customize the arguments passed to the firefox binary. Conflicting arguments with required ones to install extensions are ignored. */ + firefoxArgs?: string[]; + /** Customize the port Firefox's debugger is listening on. Defaults to a random open port. */ + firefoxRemoteDebuggingPort?: number; + /** Specify the browser to open. Defaults to `"chrome"`, but you can pass any string. */ + target?: Target; +}; + +export type ResolvedRunOptions = { + /** Absolute path to the browser binary. */ + browserBinary: string; + chromiumArgs: string[]; + chromiumRemoteDebuggingPort: number; + /** Absolute path to the directory where browser data will be stored. */ + dataDir: string; + dataPersistence: 'user' | 'project' | 'none'; + /** Absolute path to the extension directory. */ + extensionDir: string; + firefoxArgs: string[]; + firefoxRemoteDebuggingPort: number; + target: string; +}; + +export async function resolveRunOptions( + options: RunOptions | undefined, +): Promise { + debugOptions('User options:', options); + + const target = options?.target || 'chrome'; + + const _browserBinary = + options?.browserBinaries?.[target] ?? (await findBrowserBinary(target)); + if (!_browserBinary) + throw Error( + `Could not find "${target}" binary.\n\nIf it is installed in a custom location, you can specify the path with the browserPaths option.`, + ); + + // Denormalize the path so it uses the correct path separator for the OS + const browserBinary = resolve(_browserBinary); + + const chromiumRemoteDebuggingPort = options?.chromiumRemoteDebuggingPort ?? 0; + const firefoxRemoteDebuggingPort = options?.firefoxRemoteDebuggingPort ?? 0; + const dataPersistence = options?.dataPersistence ?? 'none'; + const dataDir = + dataPersistence === 'user' + ? join(homedir(), '.wxt-runner', target) + : dataPersistence === 'project' + ? options?.projectDataDir + ? resolve(options.projectDataDir) + : resolve('.wxt-runner', target) + : dataPersistence === 'none' + ? await mkdtemp(join(tmpdir(), 'wxt-runner-')) + : resolve(dataPersistence); + + const resolved: ResolvedRunOptions = { + browserBinary, + chromiumArgs: resolveChromiumArgs( + options?.chromiumArgs, + chromiumRemoteDebuggingPort, + dataDir, + ), + dataDir, + dataPersistence, + chromiumRemoteDebuggingPort, + extensionDir: resolve(options?.extensionDir ?? '.'), + firefoxArgs: resolveFirefoxArgs( + options?.firefoxArgs, + firefoxRemoteDebuggingPort, + dataDir, + ), + firefoxRemoteDebuggingPort, + target, + }; + debugOptions('Resolved options:', resolved); + return resolved; +} + +async function findBrowserBinary(target: string): Promise { + const targets = new Set([target as KnownTarget]); + FALLBACK_TARGETS[target as KnownTarget]?.forEach((fallback) => + targets.add(fallback), + ); + const platform = getPlatform(); + + for (const target of targets) { + const potentialPaths = KNOWN_BROWSER_PATHS[target]?.[platform] ?? []; + for (const path of potentialPaths) { + if (await exists(path)) return path; + } + } +} + +function getPlatform(): BrowserPlatform { + switch (process.platform) { + case 'win32': + return 'windows'; + case 'darwin': + return 'mac'; + default: + return 'linux'; + } +} + +function resolveChromiumArgs( + userArgs: string[] | undefined, + chromiumRemoteDebuggingPort: ResolvedRunOptions['chromiumRemoteDebuggingPort'], + dataDir: string, +): string[] { + return deduplicateArgs( + [ + // Limit features to improve performance + ...CHROME_LAUNCHER_DEFAULT_FLAGS, + // Enable debugging + `--remote-debugging-port=${chromiumRemoteDebuggingPort}`, + // Required for installing extensions + `--remote-debugging-pipe`, + `--user-data-dir=${dataDir}`, + `--enable-unsafe-extension-debugging`, + ], + userArgs, + { + '--remote-debugging-port': + '\x1b[1m\x1b[33mCustom Chromium --remote-debugging-port argument ignored.\x1b[0m Use \x1b[36mchromiumRemoteDebuggingPort\x1b[0m option instead.', + '--user-data-dir': + '\x1b[1m\x1b[33mCustom Chromium --user-data-dir argument ignored.\x1b[0m Use \x1b[36mdataPersistence\x1b[0m option instead.', + }, + ); +} + +function resolveFirefoxArgs( + userArgs: string[] | undefined, + firefoxRemoteDebuggingPort: ResolvedRunOptions['firefoxRemoteDebuggingPort'], + dataDir: string, +): string[] { + return deduplicateArgs( + [ + // Allows opening multiple instances of Firefox at the same time + `--new-instance`, + `--no-remote`, + `--profile`, + dataDir, + // Required for installing extensions + `--remote-debugging-port=${firefoxRemoteDebuggingPort}`, + // Default URL to start with + `about:debugging#/runtime/this-firefox`, + ], + userArgs, + { + '--remote-debugging-port': + '\x1b[1m\x1b[33mCustom Firefox --remote-debugging-port argument ignored.\x1b[0m Use \x1b[36mfirefoxDebuggerPort\x1b[0m option instead.', + '--profile': + '\x1b[1m\x1b[33mCustom Firefox --profile argument ignored.\x1b[0m Use \x1b[36mdataPersistence\x1b[0m option instead.', + }, + ); +} + +function deduplicateArgs( + requiredArgs: string[], + userArgs: string[] | undefined, + warnings: Record, +): string[] { + const getKey = (arg: string) => { + return arg.startsWith('--') ? arg.split('=')[0] : arg; + }; + const alreadyAdded = new Set(requiredArgs.map(getKey)); + + const args = [...requiredArgs]; + userArgs?.forEach((arg) => { + const key = getKey(arg); + if (alreadyAdded.has(key)) { + if (warnings[key]) console.warn(`[@wxt-dev/runner] ${warnings[key]}`); + } else { + alreadyAdded.add(key); + args.push(arg); + } + }); + + return args; +} + +async function exists(path: string): Promise { + try { + await open(path, 'r'); + return true; + } catch (err) { + // @ts-expect-error: Unknown error type + if (err?.code === 'ENOENT') return false; + throw err; + } +} + +/** + * Copied from https://github.com/GoogleChrome/chrome-launcher/blob/main/src/flags.ts + * with some flags commented out. Run tests after updating to compare. + */ +const CHROME_LAUNCHER_DEFAULT_FLAGS = [ + '--disable-features=' + + [ + // Disable built-in Google Translate service + 'Translate', + // Disable the Chrome Optimization Guide background networking + 'OptimizationHints', + // Disable the Chrome Media Router (cast target discovery) background networking + 'MediaRouter', + /// Avoid the startup dialog for _Do you want the application “Chromium.app” to accept incoming network connections?_. This is a sub-component of the MediaRouter. + 'DialMediaRouteProvider', + // Disable the feature of: Calculate window occlusion on Windows will be used in the future to throttle and potentially unload foreground tabs in occluded windows. + 'CalculateNativeWinOcclusion', + // Disables the Discover feed on NTP + 'InterestFeedContentSuggestions', + // Don't update the CT lists + 'CertificateTransparencyComponentUpdater', + // Disables autofill server communication. This feature isn't disabled via other 'parent' flags. + 'AutofillServerCommunication', + // Disables "Enhanced ad privacy in Chrome" dialog (though as of 2024-03-20 it shouldn't show up if the profile has no stored country). + 'PrivacySandboxSettings4', + ].join(','), + + // Disable some extensions that aren't affected by --disable-extensions + '--disable-component-extensions-with-background-pages', + // Disable various background network services, including extension updating, + // safe browsing service, upgrade detector, translate, UMA + '--disable-background-networking', + // Don't update the browser 'components' listed at chrome://components/ + '--disable-component-update', + // Disables client-side phishing detection. + '--disable-client-side-phishing-detection', + // Disable syncing to a Google account + '--disable-sync', + // Disable reporting to UMA, but allows for collection + '--metrics-recording-only', + // Disable installation of default apps on first run + '--disable-default-apps', + // Disable the default browser check, do not prompt to set it as such + '--no-default-browser-check', + // Skip first run wizards + '--no-first-run', + // Disable task throttling of timer tasks from background pages. + '--disable-background-timer-throttling', + // Disable the default throttling of IPC between renderer & browser processes. + '--disable-ipc-flooding-protection', + // Avoid potential instability of using Gnome Keyring or KDE wallet. crbug.com/571003 crbug.com/991424 + '--password-store=basic', + // Use mock keychain on Mac to prevent blocking permissions dialogs + '--use-mock-keychain', + // Disable background tracing (aka slow reports & deep reports) to avoid 'Tracing already started' + '--force-fieldtrials=*BackgroundTracing/default/', + + // Suppresses hang monitor dialogs in renderer processes. This flag may allow slow unload handlers on a page to prevent the tab from closing. + '--disable-hang-monitor', + // Reloading a page that came from a POST normally prompts the user. + '--disable-prompt-on-repost', + // Disables Domain Reliability Monitoring, which tracks whether the browser has difficulty contacting Google-owned sites and uploads reports to Google. + '--disable-domain-reliability', + // Disable the in-product Help (IPH) system. + '--propagate-iph-for-testing', +]; diff --git a/packages/runner/src/promises.ts b/packages/runner/src/promises.ts new file mode 100644 index 00000000..e4892b2e --- /dev/null +++ b/packages/runner/src/promises.ts @@ -0,0 +1,19 @@ +export function promiseWithResolvers(): { + promise: Promise; + resolve: (value: T) => void; + reject: (error: unknown) => void; +} { + let resolve: (value: T) => void; + let reject: (error: unknown) => void; + + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + + return { + promise, + resolve: resolve!, + reject: reject!, + }; +} diff --git a/packages/runner/src/run.ts b/packages/runner/src/run.ts new file mode 100644 index 00000000..580ac149 --- /dev/null +++ b/packages/runner/src/run.ts @@ -0,0 +1,125 @@ +import { debug } from './debug'; +import { + resolveRunOptions, + type ResolvedRunOptions, + type RunOptions, +} from './options'; +import { spawn } from 'node:child_process'; +import { installChromium, installFirefox } from './install'; +import { promiseWithResolvers } from './promises'; + +const debugFirefox = debug.scoped('firefox'); +const debugChrome = debug.scoped('chrome'); + +export interface Runner { + stop(): void; +} + +export async function run(options: RunOptions): Promise { + const resolvedOptions = await resolveRunOptions(options); + + if ( + resolvedOptions.target.includes('firefox') || + resolvedOptions.target.includes('zen') + ) { + return runFirefox(resolvedOptions); + } else { + return runChromium(resolvedOptions); + } +} + +async function runFirefox(options: ResolvedRunOptions): Promise { + const urlRes = promiseWithResolvers(); + const urlTimeout = setTimeout(() => { + urlRes.reject(Error('Timed out after 10s waiting for the browser to open')); + }, 10e3); + + // Firefox notifies the user if an instance is already running, so we don't add any logs for it. + + const browserProcess = spawn( + `"${options.browserBinary}"`, + options.firefoxArgs, + { + stdio: ['ignore', 'pipe', 'pipe'], + shell: true, + }, + ); + const debugFirefoxStderr = debugFirefox.scoped('stderr'); + browserProcess.stderr.on('data', (data: string) => { + const message = data.toString().trim(); + debugFirefoxStderr(message); + + if (message.startsWith('WebDriver BiDi listening on ws://')) { + clearTimeout(urlTimeout); + urlRes.resolve(message.slice(28)); + } + }); + const debugFirefoxStdout = debugFirefox.scoped('stdout'); + browserProcess.stdout.on('data', (data: string) => { + const message = data.toString().trim(); + debugFirefoxStdout(message); + }); + + const baseUrl = await urlRes.promise; + await installFirefox(baseUrl, options.extensionDir); + + return { + stop() { + browserProcess.kill('SIGINT'); + }, + }; +} + +async function runChromium(options: ResolvedRunOptions): Promise { + const browserProcess = spawn( + `"${options.browserBinary}"`, + options.chromiumArgs, + { + stdio: ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'], + shell: true, + }, + ); + + const opened = promiseWithResolvers(); + const openedTimeout = setTimeout(() => { + opened.reject(Error('Timed out after 10s waiting for browser to open.')); + }, 10e3); + + const debugChromeStderr = debugChrome.scoped('stderr'); + browserProcess.stderr!.on('data', (data: string) => { + const message = data.toString().trim(); + debugChromeStderr(message); + + // This message signifies Chrome started up correctly. + if (message.startsWith('DevTools listening on')) { + clearTimeout(openedTimeout); + opened.resolve(); + } + }); + const debugChromeStdout = debugChrome.scoped('stdout'); + browserProcess.stdout!.on('data', (data: string) => { + const message = data.toString().trim(); + debugChromeStdout(message); + + // This message signifies Chrome was already open, and thus we couldn't open the required new instance. + if (message === 'Opening in existing browser session.') { + clearTimeout(openedTimeout); + opened.reject( + Error( + 'An instance of the browser is already running. Close it and try again.', + ), + ); + } + }); + + // Wait for the browser to open before proceeding. + await opened.promise; + + await installChromium(browserProcess, options.extensionDir); + + return { + stop() { + browserProcess.kill('SIGINT'); + }, + }; +} diff --git a/packages/runner/src/web-socket.ts b/packages/runner/src/web-socket.ts new file mode 100644 index 00000000..baf5bc1f --- /dev/null +++ b/packages/runner/src/web-socket.ts @@ -0,0 +1,37 @@ +export function openWebSocket(url: string): Promise { + if (typeof WebSocket === 'undefined') { + throw new Error( + 'To open Firefox, your JS runtime must support the standard WebSocket API (NodeJS >=22.4.0, Bun, etc).', + ); + } + + return new Promise((resolve, reject) => { + const webSocket = new WebSocket(url); + + const cleanup = () => { + webSocket.removeEventListener('open', onOpen); + webSocket.removeEventListener('error', onError); + webSocket.removeEventListener('close', onClose); + }; + const onOpen = async () => { + cleanup(); + resolve(webSocket); + }; + const onClose = (event: CloseEvent) => { + cleanup(); + reject( + new Error( + `Connection closed: code=${event.code}, reason=${event.reason}`, + ), + ); + }; + const onError = (error: any) => { + cleanup(); + reject(new Error('Error connecting to WebSocket', { cause: error })); + }; + + webSocket.addEventListener('open', onOpen); + webSocket.addEventListener('error', onError); + webSocket.addEventListener('close', onClose); + }); +} diff --git a/packages/runner/tsconfig.json b/packages/runner/tsconfig.json new file mode 100644 index 00000000..79fb341f --- /dev/null +++ b/packages/runner/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": {} +} diff --git a/packages/runner/vitest.config.ts b/packages/runner/vitest.config.ts new file mode 100644 index 00000000..d27005d6 --- /dev/null +++ b/packages/runner/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineProject } from 'vitest/config'; + +export default defineProject({ + test: { + mockReset: true, + restoreMocks: true, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1841cc9c..44e0419b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: version: 11.0.4 '@vitest/coverage-v8': specifier: ^3.1.2 - version: 3.1.2(vitest@3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 3.2.0(vitest@3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) changelogen: specifier: ^0.6.1 version: 0.6.1(magicast@0.3.5) @@ -81,16 +81,16 @@ importers: version: 5.8.3 vitepress: specifier: ^1.6.3 - version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.87.0)(search-insights@2.15.0)(typescript@5.8.3) + version: 1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(search-insights@2.15.0)(typescript@5.8.3) vitepress-knowledge: specifier: ^0.4.1 - version: 0.4.1(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.87.0)(search-insights@2.15.0)(typescript@5.8.3)) + version: 0.4.1(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(search-insights@2.15.0)(typescript@5.8.3)) vitepress-plugin-group-icons: specifier: ^1.5.2 - version: 1.5.2 + version: 1.5.5(markdown-it@14.1.0)(vite@5.4.19(@types/node@20.17.30)(sass@1.89.1)) vitest-mock-extended: specifier: ^3.1.0 - version: 3.1.0(typescript@5.8.3)(vitest@3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 3.1.0(typescript@5.8.3)(vitest@3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) vue: specifier: ^3.5.13 version: 3.5.13(typescript@5.8.3) @@ -121,7 +121,7 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -143,7 +143,7 @@ importers: version: 2.0.0 oxlint: specifier: ^0.16.8 - version: 0.16.8 + version: 0.16.12 publint: specifier: ^0.3.12 version: 0.3.12 @@ -152,7 +152,7 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -183,7 +183,7 @@ importers: version: 5.8.3 vitest: specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) packages/i18n: dependencies: @@ -208,7 +208,7 @@ importers: version: 20.17.30 oxlint: specifier: ^0.16.8 - version: 0.16.8 + version: 0.16.12 publint: specifier: ^0.3.12 version: 0.3.12 @@ -217,13 +217,13 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) vitest: specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) vitest-plugin-random-seed: specifier: ^1.1.1 - version: 1.1.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 1.1.1(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) wxt: specifier: workspace:* version: link:../wxt @@ -232,17 +232,17 @@ importers: dependencies: '@vitejs/plugin-react': specifier: ^4.4.1 - version: 4.4.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 4.5.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) devDependencies: '@aklinker1/check': specifier: 2.0.0 version: 2.0.0 '@types/react': specifier: ^19.1.2 - version: 19.1.2 + version: 19.1.6 '@types/react-dom': specifier: ^19.1.3 - version: 19.1.3(@types/react@19.1.2) + version: 19.1.5(@types/react@19.1.6) publint: specifier: ^0.3.12 version: 0.3.12 @@ -257,7 +257,7 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -266,7 +266,7 @@ importers: dependencies: vite-plugin-solid: specifier: ^2.11.6 - version: 2.11.6(solid-js@1.9.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 2.11.6(solid-js@1.9.7)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) devDependencies: '@aklinker1/check': specifier: 2.0.0 @@ -276,13 +276,13 @@ importers: version: 0.3.12 solid-js: specifier: ^1.9.6 - version: 1.9.6 + version: 1.9.7 typescript: specifier: ^5.8.3 version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -291,7 +291,7 @@ importers: dependencies: '@sveltejs/vite-plugin-svelte': specifier: ^4.0.0 || ^5.0.0 - version: 5.0.3(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 5.0.3(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) svelte: specifier: '>=5' version: 5.1.6 @@ -307,7 +307,7 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -316,7 +316,7 @@ importers: dependencies: '@vitejs/plugin-vue': specifier: ^5.2.3 - version: 5.2.3(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + version: 5.2.3(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) devDependencies: '@aklinker1/check': specifier: 2.0.0 @@ -329,11 +329,35 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt + packages/runner: + devDependencies: + '@aklinker1/check': + specifier: 2.0.0 + version: 2.0.0 + oxlint: + specifier: ^0.16.8 + version: 0.16.12 + publint: + specifier: ^0.3.12 + version: 0.3.12 + tsx: + specifier: 4.19.4 + version: 4.19.4 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + unbuild: + specifier: ^3.5.0 + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + vitest: + specifier: ^3.1.2 + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) + packages/storage: dependencies: '@wxt-dev/browser': @@ -354,7 +378,7 @@ importers: version: 1.3.2 oxlint: specifier: ^0.16.8 - version: 0.16.8 + version: 0.16.12 publint: specifier: ^0.3.12 version: 0.3.12 @@ -363,10 +387,10 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) vitest: specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) packages/unocss: dependencies: @@ -382,7 +406,7 @@ importers: version: 2.0.0 oxlint: specifier: ^0.16.8 - version: 0.16.8 + version: 0.16.12 publint: specifier: ^0.3.12 version: 0.3.12 @@ -391,10 +415,10 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) unocss: specifier: ^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0 - version: 66.0.0(postcss@8.5.3)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + version: 66.0.0(postcss@8.5.3)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) wxt: specifier: workspace:* version: link:../wxt @@ -415,7 +439,7 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) webextension-polyfill: specifier: ^0.12.0 version: 0.12.0 @@ -553,10 +577,10 @@ importers: version: 3.13.1(rollup@4.34.9)(webpack-sources@3.2.3) vite: specifier: ^5.4.19 || ^6.3.4 - version: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) vite-node: specifier: ^2.1.4 || ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) web-ext-run: specifier: ^0.2.3 version: 0.2.3 @@ -587,13 +611,13 @@ importers: version: 2.0.1 happy-dom: specifier: ^17.4.6 - version: 17.4.6 + version: 17.5.6 lodash.merge: specifier: ^4.6.2 version: 4.6.2 oxlint: specifier: ^0.16.8 - version: 0.16.8 + version: 0.16.12 publint: specifier: ^0.3.12 version: 0.3.12 @@ -602,13 +626,13 @@ importers: version: 5.8.3 unbuild: specifier: ^3.5.0 - version: 3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + version: 3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) vitest: specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) vitest-plugin-random-seed: specifier: ^1.1.1 - version: 1.1.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 1.1.1(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) packages/wxt-demo: dependencies: @@ -624,10 +648,10 @@ importers: devDependencies: '@types/react': specifier: ^19.1.2 - version: 19.1.2 + version: 19.1.6 '@types/react-dom': specifier: ^19.1.3 - version: 19.1.3(@types/react@19.1.2) + version: 19.1.5(@types/react@19.1.6) '@wxt-dev/auto-icons': specifier: workspace:* version: link:../auto-icons @@ -636,19 +660,19 @@ importers: version: link:../unocss sass: specifier: ^1.87.0 - version: 1.87.0 + version: 1.89.1 typescript: specifier: ^5.8.3 version: 5.8.3 unocss: specifier: ^0.64.0 || ^0.65.0 || ^65.0.0 || ^66.0.0 - version: 66.0.0(postcss@8.5.3)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + version: 66.0.0(postcss@8.5.3)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) vitest: specifier: ^3.1.2 - version: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + version: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) vitest-plugin-random-seed: specifier: ^1.1.1 - version: 1.1.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + version: 1.1.1(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) wxt: specifier: workspace:* version: link:../wxt @@ -773,32 +797,32 @@ packages: resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.1': - resolution: {integrity: sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A==} + '@babel/compat-data@7.27.3': + resolution: {integrity: sha512-V42wFfx1ymFte+ecf6iXghnnP8kWTO+ZLXIyZq+1LAXHHvTZdVxicn4yiVYdYMGaCO3tmqub11AorKkv+iodqw==} engines: {node: '>=6.9.0'} '@babel/core@7.26.9': resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.1': - resolution: {integrity: sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==} + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} engines: {node: '>=6.9.0'} '@babel/generator@7.26.9': resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.27.1': - resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + '@babel/generator@7.27.3': + resolution: {integrity: sha512-xnlJYj5zepml8NXtjkG0WquFUv8RskFqyFcVgTBp5k+NaA/8uw/K+OSVf8AMGw5e9HKP2ETd5xpK5MLZQD6b4Q==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.26.5': resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.27.1': - resolution: {integrity: sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.18.6': @@ -819,8 +843,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-module-transforms@7.27.1': - resolution: {integrity: sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==} + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -853,12 +877,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.10': - resolution: {integrity: sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==} + '@babel/helpers@7.26.9': + resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.27.1': - resolution: {integrity: sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==} + '@babel/helpers@7.27.4': + resolution: {integrity: sha512-Y+bO6U+I7ZKaM5G5rDUZiYfUvQPUibYmAFe7EnKdnKBbVXDZxvp+MWOH5gYciY0EPk4EScsuFMQBbEfpdRKSCQ==} engines: {node: '>=6.9.0'} '@babel/parser@7.26.9': @@ -866,8 +890,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/parser@7.27.1': - resolution: {integrity: sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==} + '@babel/parser@7.27.4': + resolution: {integrity: sha512-BRmLHGwpUqLFR2jzx9orBuX/ABDkj2jLKOXrHDTN2aOKL+jFDDKaRNo9nyYsIl9h/UE/7lMKdDjKQQyxKKDZ7g==} engines: {node: '>=6.0.0'} hasBin: true @@ -897,24 +921,24 @@ packages: resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} - '@babel/template@7.27.1': - resolution: {integrity: sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} '@babel/traverse@7.26.9': resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.1': - resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.0': - resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} + '@babel/types@7.26.9': + resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.1': - resolution: {integrity: sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==} + '@babel/types@7.27.3': + resolution: {integrity: sha512-Y1GkI4ktrtvmawoSq+4FCVHNryea6uR+qUQy0AGxLSsjCX0nVmkYQMBLHDkXZuo5hGx7eYdnIaslsdBFm7zbUw==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': @@ -965,8 +989,8 @@ packages: search-insights: optional: true - '@emnapi/runtime@1.4.0': - resolution: {integrity: sha512-64WYIf4UYcdLnbKn/umDlNjQDSS8AgZrI/R9+x5ilkUVFxXcA1Ebl+gQLc/6mERA4407Xof0R7wEyEuj091CVw==} + '@emnapi/runtime@1.4.1': + resolution: {integrity: sha512-LMshMVP0ZhACNjQNYXiU1iZJ6QCcv0lUdPDPugqGvCGXt5xtRVBPdtA0qU12pEXZzpWAhWlZYptfdAFq10DOVQ==} '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} @@ -1580,43 +1604,43 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxlint/darwin-arm64@0.16.8': - resolution: {integrity: sha512-1w7Jbrp0WUkbXlGb/x3nh7MK8EzCS8USR2Q8Yo0ZMmCNXmxxfkL07B9wSmlKcJ7y9MSfFkpF4Md5r6jwJNeWLg==} + '@oxlint/darwin-arm64@0.16.12': + resolution: {integrity: sha512-G7phYhlIA4ke2nW7tHLl+E5+rvdzgGA6830D+e+y1RGllT0w2ONGdKcVTj+2pXGCw6yPmCC5fDsDEn2+RPTfxg==} cpu: [arm64] os: [darwin] - '@oxlint/darwin-x64@0.16.8': - resolution: {integrity: sha512-zWMKa8kNmtQfBHG5j4HGiOT1azjgs4rFsjAADWHBQ6qsqdW0evhQt9666JYJA2kKtuoWiIPv4UypV5s2QI9oCQ==} + '@oxlint/darwin-x64@0.16.12': + resolution: {integrity: sha512-P/LSOgJ6SzQ3OKEIf3HsebgokZiZ5nDuTgIL4LpNCHlkOLDu/fT8XL9pSkR5y+60v0SOxUF/+aN0Q8EmxblrCw==} cpu: [x64] os: [darwin] - '@oxlint/linux-arm64-gnu@0.16.8': - resolution: {integrity: sha512-AK6x1UN/Wz0C+kpV70J3cKgKl+b6Jrh4EDAY6JY99F7DMlNAbKtRtBZAb3IMiuukv/mqbTvb7Z7+dUIp/DHOkA==} + '@oxlint/linux-arm64-gnu@0.16.12': + resolution: {integrity: sha512-0N/ZsW+cL7ZAUvOHbzMp3iApt5b/Q81q2e9RgEzkI6gUDCJK8/blWg0se/i6y9e24WH0ZC4bcxY1+Qz4ZQ+mFw==} cpu: [arm64] os: [linux] - '@oxlint/linux-arm64-musl@0.16.8': - resolution: {integrity: sha512-+VJNo9NGYjP/8qFvcMk6IR4waRENbXolDk6SsfWXnub/b0UpXd8dH+cFSo9VPsHoLQ1+/A2WbqTxO+XkkaUXYw==} + '@oxlint/linux-arm64-musl@0.16.12': + resolution: {integrity: sha512-MoG1SIw4RGowsOsPjm5HjRWymisRZWBea7ewMoXA5xIVQ3eqECifG0KJW0OZp96Ad8DFBEavdlNuImB2uXsMwg==} cpu: [arm64] os: [linux] - '@oxlint/linux-x64-gnu@0.16.8': - resolution: {integrity: sha512-1AN2P3AUfW04jmaiEh/NnV3/oZKrXMScqbihZ8bclKdwKanEyNX+McCfpUZ6/L1h1imCj9tR1ChNq0LaPrJfAg==} + '@oxlint/linux-x64-gnu@0.16.12': + resolution: {integrity: sha512-STho8QdMLfn/0lqRU94tGPaYX8lGJccPbqeUcEr3eK5gZ5ZBdXmiHlvkcngXFEXksYC8/5VoJN7Vf3HsmkEskw==} cpu: [x64] os: [linux] - '@oxlint/linux-x64-musl@0.16.8': - resolution: {integrity: sha512-FyG47d0ypwpIv+OEgTQe+qV9UDgeEoNJW0p50/zUkQhe3LVC20qNztvFp0X9uTm1V7ihY8GCZKC1ooeD3+eWWw==} + '@oxlint/linux-x64-musl@0.16.12': + resolution: {integrity: sha512-i7pzSoj9nCg/ZzOe8dCZeFWyRRWDylR9tIX04xRTq3G6PBLm6i9VrOdEkxbgM9+pCkRzUc0a9D7rbtCF34TQUA==} cpu: [x64] os: [linux] - '@oxlint/win32-arm64@0.16.8': - resolution: {integrity: sha512-fUnKMhcyChO7nLh5TS/yELOx7FG1827GShIalQr6L9SnDGeKjjzBrJwpc3jFtTB4Ti6wX+7yB7JYOo2EgoRdvQ==} + '@oxlint/win32-arm64@0.16.12': + resolution: {integrity: sha512-wcxq3IBJ7ZlONlXJxQM+7EMx+LX1nkz3ZS3R0EtDM76EOZaqe8BMkW5cXVhF8jarZTZC18oKAckW4Ng9d8adBg==} cpu: [arm64] os: [win32] - '@oxlint/win32-x64@0.16.8': - resolution: {integrity: sha512-gpvVsWH8HJQP6UWGyDlZdslmk6CNTtjEl8MLD6Mhx+2L02ypaYkA0pyTKPaLQrn0O2D+CT0WAh53YtxCf903qA==} + '@oxlint/win32-x64@0.16.12': + resolution: {integrity: sha512-Ae1fx7wmAcMVqzS8rLINaFRpAdh29QzHh133bEYMHzfWBYyK/hLu9g4GLwC/lEIVQu9884b8qutGfdOk6Qia3w==} cpu: [x64] os: [win32] @@ -1725,6 +1749,9 @@ packages: resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==} engines: {node: '>=18'} + '@rolldown/pluginutils@1.0.0-beta.9': + resolution: {integrity: sha512-e9MeMtVWo186sgvFFJOPGy7/d2j2mZhLJIdVW0C/xDluuOvymEATqz6zKsP0ZmXGzQtqlyjz5sC1sYQUoJG98w==} + '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} engines: {node: '>=14.0.0'} @@ -1929,12 +1956,18 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chrome@0.0.326': resolution: {integrity: sha512-WS7jKf3ZRZFHOX7dATCZwqNJgdfiSF0qBRFxaO0LhIOvTNBrfkab26bsZwp6EBpYtqp8loMHJTnD6vDTLWPKYw==} '@types/conventional-commits-parser@5.0.1': resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1989,16 +2022,16 @@ packages: '@types/prop-types@15.7.14': resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - '@types/react-dom@19.1.3': - resolution: {integrity: sha512-rJXC08OG0h3W6wDMFxQrZF00Kq6qQvw0djHRdzl3U5DnIERz0MRce3WVc7IS6JYBwtaP/DwYtRRjVlvivNveKg==} + '@types/react-dom@19.1.5': + resolution: {integrity: sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==} peerDependencies: '@types/react': ^19.0.0 '@types/react@18.3.12': resolution: {integrity: sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==} - '@types/react@19.1.2': - resolution: {integrity: sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==} + '@types/react@19.1.6': + resolution: {integrity: sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -2104,8 +2137,8 @@ packages: peerDependencies: vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 - '@vitejs/plugin-react@4.4.1': - resolution: {integrity: sha512-IpEm5ZmeXAP/osiBXVVP5KjFMzbWOonMs0NaQQl+xYnUAcq4oHUBsF2+p4MgKWG4YMmFYJU8A6sxRPuowllm6w==} + '@vitejs/plugin-react@4.5.0': + resolution: {integrity: sha512-JuLWaEqypaJmOJPLWwO335Ig6jSgC1FTONCWAxnqcQthLTK/Yc9aH6hr9z/87xciejbQcnP3GnA1FWUSWeXaeg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 @@ -2117,43 +2150,43 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/coverage-v8@3.1.2': - resolution: {integrity: sha512-XDdaDOeaTMAMYW7N63AqoK32sYUWbXnTkC6tEbVcu3RlU1bB9of32T+PGf8KZvxqLNqeXhafDFqCkwpf2+dyaQ==} + '@vitest/coverage-v8@3.2.0': + resolution: {integrity: sha512-HjgvaokAiHxRMI5ioXl4WmgAi4zQtKtnltOOlmpzUqApdcTTZrZJAastbbRGydtiqwtYLFaIb6Jpo3PzowZ0cg==} peerDependencies: - '@vitest/browser': 3.1.2 - vitest: 3.1.2 + '@vitest/browser': 3.2.0 + vitest: 3.2.0 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@3.1.2': - resolution: {integrity: sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==} + '@vitest/expect@3.2.0': + resolution: {integrity: sha512-0v4YVbhDKX3SKoy0PHWXpKhj44w+3zZkIoVES9Ex2pq+u6+Bijijbi2ua5kE+h3qT6LBWFTNZSCOEU37H8Y5sA==} - '@vitest/mocker@3.1.2': - resolution: {integrity: sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==} + '@vitest/mocker@3.2.0': + resolution: {integrity: sha512-HFcW0lAMx3eN9vQqis63H0Pscv0QcVMo1Kv8BNysZbxcmHu3ZUYv59DS6BGYiGQ8F5lUkmsfMMlPm4DJFJdf/A==} peerDependencies: msw: ^2.4.9 - vite: ^5.0.0 || ^6.0.0 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@3.1.2': - resolution: {integrity: sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==} + '@vitest/pretty-format@3.2.0': + resolution: {integrity: sha512-gUUhaUmPBHFkrqnOokmfMGRBMHhgpICud9nrz/xpNV3/4OXCn35oG+Pl8rYYsKaTNd/FAIrqRHnwpDpmYxCYZw==} - '@vitest/runner@3.1.2': - resolution: {integrity: sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==} + '@vitest/runner@3.2.0': + resolution: {integrity: sha512-bXdmnHxuB7fXJdh+8vvnlwi/m1zvu+I06i1dICVcDQFhyV4iKw2RExC/acavtDn93m/dRuawUObKsrNE1gJacA==} - '@vitest/snapshot@3.1.2': - resolution: {integrity: sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==} + '@vitest/snapshot@3.2.0': + resolution: {integrity: sha512-z7P/EneBRMe7hdvWhcHoXjhA6at0Q4ipcoZo6SqgxLyQQ8KSMMCmvw1cSt7FHib3ozt0wnRHc37ivuUMbxzG/A==} - '@vitest/spy@3.1.2': - resolution: {integrity: sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==} + '@vitest/spy@3.2.0': + resolution: {integrity: sha512-s3+TkCNUIEOX99S0JwNDfsHRaZDDZZR/n8F0mop0PmsEbQGKZikCGpTGZ6JRiHuONKew3Fb5//EPwCP+pUX9cw==} - '@vitest/utils@3.1.2': - resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==} + '@vitest/utils@3.2.0': + resolution: {integrity: sha512-gXXOe7Fj6toCsZKVQouTRLJftJwmvbhH5lKOBR6rlP950zUq9AitTUjnFoXS/CqjBC2aoejAztLPzzuva++XBw==} '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} @@ -2308,6 +2341,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -2327,6 +2363,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.3: + resolution: {integrity: sha512-MuXMrSLVVoA6sYN/6Hke18vMzrT4TZNbZIj/hvh0fnYFpO+/kFXcLIaiPwXXWaQUPg4yJD8fj+lfJ7/1EBconw==} + async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} @@ -2620,8 +2659,8 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cross-spawn@7.0.5: - resolution: {integrity: sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==} + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} css-declaration-sorter@7.2.0: @@ -2711,6 +2750,15 @@ packages: supports-color: optional: true + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + deep-eql@5.0.2: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} @@ -2844,8 +2892,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es-module-lexer@1.6.0: - resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} es6-error@4.1.1: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} @@ -2934,8 +2982,16 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - fdir@6.4.4: - resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fdir@6.4.5: + resolution: {integrity: sha512-4BG7puHpVsIYxZUbiUE3RqGloLaSSwzYie5jvasC4LWuBWzZawynvYouhjbQKw2JuIGYdm0DzIxl8iVidKlUEw==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -3065,8 +3121,8 @@ packages: resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} engines: {node: '>=10'} - happy-dom@17.4.6: - resolution: {integrity: sha512-OEV1hDe9i2rFr66+WZNiwy1S8rAJy6bRXmXql68YJDjdfHBRbN76om+qVh68vQACf6y5Bcr90e/oK53RQxsDdg==} + happy-dom@17.5.6: + resolution: {integrity: sha512-B4U6jKuiizwCJ2WP0YreQmRdeBrHKOXhpz7YUbbwdSAKfWEhdG4UfWZOZTZ5Oejs/9yJtk7xmbfp8YdVL9LVFA==} engines: {node: '>=18.0.0'} has-flag@4.0.0: @@ -3325,6 +3381,9 @@ packages: js-tokens@9.0.0: resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -3387,8 +3446,8 @@ packages: linkedom@0.18.10: resolution: {integrity: sha512-ESCqVAtme2GI3zZnlVRidiydByV6WmPlmKeFzFVQslADiAO2Wi+H6xL/5kr/pUOESjEoVb2Eb3cYFJ/TQhQOWA==} - linkedom@0.18.9: - resolution: {integrity: sha512-Pfvhwjs46nBrcQdauQjMXDJZqj6VwN7KStT84xQqmIgD9bPH6UVJ/ESW8y4VHVF2h7di0/P+f4Iln4U5emRcmg==} + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} lint-staged@15.5.1: resolution: {integrity: sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==} @@ -3481,6 +3540,10 @@ packages: markdown-it-footnote@4.0.0: resolution: {integrity: sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==} + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + marked@4.3.0: resolution: {integrity: sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==} engines: {node: '>= 12'} @@ -3501,6 +3564,9 @@ packages: mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + merge-anything@5.1.7: resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} engines: {node: '>=12.13'} @@ -3700,10 +3766,6 @@ packages: oniguruma-to-es@3.1.1: resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} - open@10.1.0: - resolution: {integrity: sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==} - engines: {node: '>=18'} - open@10.1.2: resolution: {integrity: sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==} engines: {node: '>=18'} @@ -3728,8 +3790,8 @@ packages: resolution: {integrity: sha512-jd0cvB8qQ5uVt0lvCIexBaROw1KyKm5sbulg2fWOHjETisuCzWyt+eTZKEMs8v6HwzoGs8xik26jg7eCM6pS+A==} engines: {node: '>= 0.4.0'} - oxlint@0.16.8: - resolution: {integrity: sha512-UmpfqkynkudIY8orqAvYelQXJ1YNMecwJ422ZtJ84vbtIQ2HH96WBhqLAEoUgDB7yu/LpxMQrjukFOJQkh+mQw==} + oxlint@0.16.12: + resolution: {integrity: sha512-1oN3P9bzE90zkbjLTc+uICVLwSR+eQaDaYVipS0BtmtmEd3ccQue0y7npCinb35YqKzIv1LZxhoU9nm5fgmQuw==} engines: {node: '>=8.*'} hasBin: true @@ -4059,6 +4121,10 @@ packages: pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + pupa@3.1.0: resolution: {integrity: sha512-FLpr4flz5xZTSJxSeaheeMKN/EDzMdK7b8PTOC6a5PYFKTucWbdqjgqaEyH0shFiSJrVB1+Qqi4Tk19ccU6Aug==} engines: {node: '>=12.20'} @@ -4204,8 +4270,8 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - sass@1.87.0: - resolution: {integrity: sha512-d0NoFH4v6SjEK7BoX810Jsrhj7IQSYHAHLi/iSpgqKc7LaIDshFRlSg5LOymf9FqQhxEHs2W5ZQXlvy0KD45Uw==} + sass@1.89.1: + resolution: {integrity: sha512-eMLLkl+qz7tx/0cJ9wI+w09GQ2zodTkcE/aVfywwdlRcI3EO19xGnbmJwg/JMIm+5MxVJ6outddLZ4Von4E++Q==} engines: {node: '>=14.0.0'} hasBin: true @@ -4233,14 +4299,14 @@ packages: engines: {node: '>=10'} hasBin: true - seroval-plugins@1.2.1: - resolution: {integrity: sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw==} + seroval-plugins@1.3.2: + resolution: {integrity: sha512-0QvCV2lM3aj/U3YozDiVwx9zpH0q8A60CTWIv4Jszj/givcudPb48B+rkU5D51NJ0pTpweGMttHjboPa9/zoIQ==} engines: {node: '>=10'} peerDependencies: seroval: ^1.0 - seroval@1.2.1: - resolution: {integrity: sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw==} + seroval@1.3.2: + resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==} engines: {node: '>=10'} set-value@4.1.0: @@ -4306,8 +4372,8 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} - solid-js@1.9.6: - resolution: {integrity: sha512-PoasAJvLk60hRtOTe9ulvALOdLjjqxuxcGZRolBQqxOnXrBXHGzqMT4ijNhGsDAYdOgEa8ZYaAE94PSldrFSkA==} + solid-js@1.9.7: + resolution: {integrity: sha512-/saTKi8iWEM233n5OSi1YHCCuh66ZIQ7aK2hsToPe4tqGm7qAejU1SwNuTPivbWAYq7SjuHVVYxxuZQNRbICiw==} solid-refresh@0.6.3: resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} @@ -4352,6 +4418,9 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + std-env@3.8.1: + resolution: {integrity: sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==} + std-env@3.9.0: resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} @@ -4472,20 +4541,24 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} - tinypool@1.0.2: - resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + tinyglobby@0.2.14: + resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.0: + resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@2.0.0: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} titleize@3.0.0: @@ -4527,8 +4600,8 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-fest@4.40.1: - resolution: {integrity: sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} typedarray@0.0.6: @@ -4565,6 +4638,9 @@ packages: resolution: {integrity: sha512-z6PJ8Lml+v3ichVojCiB8toQJBuwR42ySM4ezjXIqXK3M0HczmKQ3LF4rhU55PfD99KEEXQG6yb7iOMyvYuHew==} hasBin: true + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + ufo@1.5.4: resolution: {integrity: sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==} @@ -4667,8 +4743,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite-node@3.1.2: - resolution: {integrity: sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==} + vite-node@3.2.0: + resolution: {integrity: sha512-8Fc5Ko5Y4URIJkmMF/iFP1C0/OJyY+VGVe9Nw6WAdZyw4bTO+eVg9mwxWkQp/y8NnAoQY3o9KAvE1ZdA2v+Vmg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -4713,8 +4789,8 @@ packages: terser: optional: true - vite@6.3.4: - resolution: {integrity: sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==} + vite@6.3.5: + resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -4766,8 +4842,11 @@ packages: peerDependencies: vitepress: ^1.0.0 - vitepress-plugin-group-icons@1.5.2: - resolution: {integrity: sha512-zen07KxZ83y3eecou4EraaEgwIriwHaB5Q0cHAmS4yO1UZEQvbljTylHPqiJ7LNkV39U8VehfcyquAJXg/26LA==} + vitepress-plugin-group-icons@1.5.5: + resolution: {integrity: sha512-eVnBL3lVOYxByQg5xo44QZtGPv41JyxWI7YxuwrGcNUU+W8MMIjb9XlivBXb3W8CosFllJlLGiqNCBTnFZHFTA==} + peerDependencies: + markdown-it: '>=14' + vite: '>=3' vitepress@1.6.3: resolution: {integrity: sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw==} @@ -4792,16 +4871,16 @@ packages: peerDependencies: vite: ^4.0.0 || ^5.0.0 || ^6.0.0 - vitest@3.1.2: - resolution: {integrity: sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==} + vitest@3.2.0: + resolution: {integrity: sha512-P7Nvwuli8WBNmeMHHek7PnGW4oAZl9za1fddfRVidZar8wDZRi7hpznLKQePQ8JPLwSBEYDK11g+++j7uFJV8Q==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.2 - '@vitest/ui': 3.1.2 + '@vitest/browser': 3.2.0 + '@vitest/ui': 3.2.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -5148,7 +5227,7 @@ snapshots: '@babel/compat-data@7.26.8': {} - '@babel/compat-data@7.27.1': {} + '@babel/compat-data@7.27.3': {} '@babel/core@7.26.9': dependencies: @@ -5157,11 +5236,11 @@ snapshots: '@babel/generator': 7.26.9 '@babel/helper-compilation-targets': 7.26.5 '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) - '@babel/helpers': 7.26.10 + '@babel/helpers': 7.26.9 '@babel/parser': 7.26.9 '@babel/template': 7.26.9 '@babel/traverse': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -5170,18 +5249,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/core@7.27.1': + '@babel/core@7.27.4': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/helper-compilation-targets': 7.27.1 - '@babel/helper-module-transforms': 7.27.1(@babel/core@7.27.1) - '@babel/helpers': 7.27.1 - '@babel/parser': 7.27.1 - '@babel/template': 7.27.1 - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/generator': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.4 + '@babel/parser': 7.27.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.3 convert-source-map: 2.0.0 debug: 4.4.0 gensync: 1.0.0-beta.2 @@ -5193,15 +5272,15 @@ snapshots: '@babel/generator@7.26.9': dependencies: '@babel/parser': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 - '@babel/generator@7.27.1': + '@babel/generator@7.27.3': dependencies: - '@babel/parser': 7.27.1 - '@babel/types': 7.27.1 + '@babel/parser': 7.27.4 + '@babel/types': 7.27.3 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 @@ -5214,9 +5293,9 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-compilation-targets@7.27.1': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.1 + '@babel/compat-data': 7.27.3 '@babel/helper-validator-option': 7.27.1 browserslist: 4.24.4 lru-cache: 5.1.1 @@ -5224,19 +5303,19 @@ snapshots: '@babel/helper-module-imports@7.18.6': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 '@babel/helper-module-imports@7.25.9': dependencies: '@babel/traverse': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.1 - '@babel/types': 7.27.1 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.3 transitivePeerDependencies: - supports-color @@ -5249,12 +5328,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.1(@babel/core@7.27.1)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.27.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.1 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color @@ -5272,37 +5351,37 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.26.10': + '@babel/helpers@7.26.9': dependencies: '@babel/template': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 - '@babel/helpers@7.27.1': + '@babel/helpers@7.27.4': dependencies: - '@babel/template': 7.27.1 - '@babel/types': 7.27.1 + '@babel/template': 7.27.2 + '@babel/types': 7.27.3 '@babel/parser@7.26.9': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 - '@babel/parser@7.27.1': + '@babel/parser@7.27.4': dependencies: - '@babel/types': 7.27.1 + '@babel/types': 7.27.3 '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.9)': dependencies: '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.27.1)': + '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.26.5 - '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.27.1)': + '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.27.1 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.26.5 '@babel/runtime@7.27.0': @@ -5313,13 +5392,13 @@ snapshots: dependencies: '@babel/code-frame': 7.26.2 '@babel/parser': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 - '@babel/template@7.27.1': + '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.1 - '@babel/types': 7.27.1 + '@babel/parser': 7.27.4 + '@babel/types': 7.27.3 '@babel/traverse@7.26.9': dependencies: @@ -5327,30 +5406,30 @@ snapshots: '@babel/generator': 7.26.9 '@babel/parser': 7.26.9 '@babel/template': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/traverse@7.27.1': + '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.27.1 - '@babel/parser': 7.27.1 - '@babel/template': 7.27.1 - '@babel/types': 7.27.1 + '@babel/generator': 7.27.3 + '@babel/parser': 7.27.4 + '@babel/template': 7.27.2 + '@babel/types': 7.27.3 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.0': + '@babel/types@7.26.9': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/types@7.27.1': + '@babel/types@7.27.3': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -5410,7 +5489,7 @@ snapshots: transitivePeerDependencies: - '@algolia/client-search' - '@emnapi/runtime@1.4.0': + '@emnapi/runtime@1.4.1': dependencies: tslib: 2.6.0 optional: true @@ -5732,7 +5811,7 @@ snapshots: '@img/sharp-wasm32@0.34.1': dependencies: - '@emnapi/runtime': 1.4.0 + '@emnapi/runtime': 1.4.1 optional: true '@img/sharp-win32-ia32@0.34.1': @@ -5789,28 +5868,28 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - '@oxlint/darwin-arm64@0.16.8': + '@oxlint/darwin-arm64@0.16.12': optional: true - '@oxlint/darwin-x64@0.16.8': + '@oxlint/darwin-x64@0.16.12': optional: true - '@oxlint/linux-arm64-gnu@0.16.8': + '@oxlint/linux-arm64-gnu@0.16.12': optional: true - '@oxlint/linux-arm64-musl@0.16.8': + '@oxlint/linux-arm64-musl@0.16.12': optional: true - '@oxlint/linux-x64-gnu@0.16.8': + '@oxlint/linux-x64-gnu@0.16.12': optional: true - '@oxlint/linux-x64-musl@0.16.8': + '@oxlint/linux-x64-musl@0.16.12': optional: true - '@oxlint/win32-arm64@0.16.8': + '@oxlint/win32-arm64@0.16.12': optional: true - '@oxlint/win32-x64@0.16.8': + '@oxlint/win32-x64@0.16.12': optional: true '@parcel/watcher-android-arm64@2.5.0': @@ -5893,6 +5972,8 @@ snapshots: '@publint/pack@0.1.2': {} + '@rolldown/pluginutils@1.0.0-beta.9': {} + '@rollup/plugin-alias@5.1.1(rollup@4.34.9)': optionalDependencies: rollup: 4.34.9 @@ -5902,7 +5983,7 @@ snapshots: '@rollup/pluginutils': 5.1.4(rollup@4.34.9) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.4.4(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) is-reference: 1.2.1 magic-string: 0.30.17 picomatch: 4.0.2 @@ -6037,25 +6118,25 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} - '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)))(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))': + '@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)))(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + '@sveltejs/vite-plugin-svelte': 5.0.3(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) debug: 4.4.0 svelte: 5.1.6 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))': + '@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)))(svelte@5.1.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + '@sveltejs/vite-plugin-svelte-inspector': 4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)))(svelte@5.1.6)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) debug: 4.4.0 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.17 svelte: 5.1.6 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) - vitefu: 1.0.6(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) + vitefu: 1.0.6(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) transitivePeerDependencies: - supports-color @@ -6064,23 +6145,27 @@ snapshots: '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 '@types/babel__generator@7.6.8': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 '@types/babel__traverse@7.20.6': dependencies: - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 + + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 '@types/chrome@0.0.326': dependencies: @@ -6091,6 +6176,8 @@ snapshots: dependencies: '@types/node': 20.17.30 + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.6': {} '@types/filesystem@0.0.36': @@ -6149,9 +6236,9 @@ snapshots: '@types/prop-types@15.7.14': optional: true - '@types/react-dom@19.1.3(@types/react@19.1.2)': + '@types/react-dom@19.1.5(@types/react@19.1.6)': dependencies: - '@types/react': 19.1.2 + '@types/react': 19.1.6 '@types/react@18.3.12': dependencies: @@ -6159,7 +6246,7 @@ snapshots: csstype: 3.1.3 optional: true - '@types/react@19.1.2': + '@types/react@19.1.6': dependencies: csstype: 3.1.3 @@ -6180,13 +6267,13 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unocss/astro@66.0.0(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': + '@unocss/astro@66.0.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': dependencies: '@unocss/core': 66.0.0 '@unocss/reset': 66.0.0 - '@unocss/vite': 66.0.0(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + '@unocss/vite': 66.0.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) optionalDependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - vue @@ -6203,7 +6290,7 @@ snapshots: magic-string: 0.30.17 pathe: 2.0.3 perfect-debounce: 1.0.0 - tinyglobby: 0.2.13 + tinyglobby: 0.2.12 unplugin-utils: 0.2.4 '@unocss/config@66.0.0': @@ -6235,7 +6322,7 @@ snapshots: '@unocss/rule-utils': 66.0.0 css-tree: 3.1.0 postcss: 8.5.3 - tinyglobby: 0.2.13 + tinyglobby: 0.2.12 '@unocss/preset-attributify@66.0.0': dependencies: @@ -6311,7 +6398,7 @@ snapshots: dependencies: '@unocss/core': 66.0.0 - '@unocss/vite@66.0.0(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': + '@unocss/vite@66.0.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': dependencies: '@ampproject/remapping': 2.3.0 '@unocss/config': 66.0.0 @@ -6319,38 +6406,40 @@ snapshots: '@unocss/inspector': 66.0.0(vue@3.5.13(typescript@5.8.3)) chokidar: 3.6.0 magic-string: 0.30.17 - tinyglobby: 0.2.13 + tinyglobby: 0.2.12 unplugin-utils: 0.2.4 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - vue - '@vitejs/plugin-react@4.4.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))': + '@vitejs/plugin-react@4.5.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))': dependencies: - '@babel/core': 7.27.1 - '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.27.1) - '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.27.1) + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.27.4) + '@rolldown/pluginutils': 1.0.0-beta.9 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.3(vite@5.4.19(@types/node@20.17.30)(sass@1.87.0))(vue@3.5.13(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.3(vite@5.4.19(@types/node@20.17.30)(sass@1.89.1))(vue@3.5.13(typescript@5.8.3))': dependencies: - vite: 5.4.19(@types/node@20.17.30)(sass@1.87.0) + vite: 5.4.19(@types/node@20.17.30)(sass@1.89.1) vue: 3.5.13(typescript@5.8.3) - '@vitejs/plugin-vue@5.2.3(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.3(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3))': dependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) vue: 3.5.13(typescript@5.8.3) - '@vitest/coverage-v8@3.1.2(vitest@3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))': + '@vitest/coverage-v8@3.2.0(vitest@3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 - debug: 4.4.0 + ast-v8-to-istanbul: 0.3.3 + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 @@ -6360,47 +6449,48 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vitest: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - supports-color - '@vitest/expect@3.1.2': + '@vitest/expect@3.2.0': dependencies: - '@vitest/spy': 3.1.2 - '@vitest/utils': 3.1.2 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.0 + '@vitest/utils': 3.2.0 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.2(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))': + '@vitest/mocker@3.2.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))': dependencies: - '@vitest/spy': 3.1.2 + '@vitest/spy': 3.2.0 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) - '@vitest/pretty-format@3.1.2': + '@vitest/pretty-format@3.2.0': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.1.2': + '@vitest/runner@3.2.0': dependencies: - '@vitest/utils': 3.1.2 + '@vitest/utils': 3.2.0 pathe: 2.0.3 - '@vitest/snapshot@3.1.2': + '@vitest/snapshot@3.2.0': dependencies: - '@vitest/pretty-format': 3.1.2 + '@vitest/pretty-format': 3.2.0 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.1.2': + '@vitest/spy@3.2.0': dependencies: - tinyspy: 3.0.2 + tinyspy: 4.0.3 - '@vitest/utils@3.1.2': + '@vitest/utils@3.2.0': dependencies: - '@vitest/pretty-format': 3.1.2 + '@vitest/pretty-format': 3.2.0 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -6566,6 +6656,8 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + argparse@2.0.1: {} + aria-query@5.3.2: {} array-differ@4.0.0: {} @@ -6576,6 +6668,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@0.3.3: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + async-mutex@0.5.0: dependencies: tslib: 2.6.0 @@ -6606,7 +6704,7 @@ snapshots: '@babel/core': 7.26.9 '@babel/helper-module-imports': 7.18.6 '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.9) - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 html-entities: 2.3.3 parse5: 7.2.1 validate-html-nesting: 1.2.2 @@ -6643,7 +6741,7 @@ snapshots: chalk: 5.4.1 cli-boxes: 3.0.0 string-width: 7.2.0 - type-fest: 4.40.1 + type-fest: 4.41.0 widest-line: 5.0.0 wrap-ansi: 9.0.0 @@ -6744,12 +6842,12 @@ snapshots: mri: 1.2.0 node-fetch-native: 1.6.6 ofetch: 1.4.1 - open: 10.1.0 + open: 10.1.2 pathe: 2.0.3 pkg-types: 2.1.0 scule: 1.3.0 semver: 7.7.1 - std-env: 3.9.0 + std-env: 3.8.1 transitivePeerDependencies: - magicast @@ -6910,7 +7008,7 @@ snapshots: core-util-is@1.0.3: {} - cross-spawn@7.0.5: + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 @@ -7013,6 +7111,10 @@ snapshots: dependencies: ms: 2.1.3 + debug@4.4.1: + dependencies: + ms: 2.1.3 + deep-eql@5.0.2: {} deep-extend@0.6.0: {} @@ -7087,7 +7189,7 @@ snapshots: dot-prop@9.0.0: dependencies: - type-fest: 4.40.1 + type-fest: 4.41.0 dotenv-expand@12.0.2: dependencies: @@ -7123,7 +7225,7 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es-module-lexer@1.6.0: {} + es-module-lexer@1.7.0: {} es6-error@4.1.1: {} @@ -7234,7 +7336,7 @@ snapshots: execa@5.1.1: dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.3 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -7246,7 +7348,7 @@ snapshots: execa@7.2.0: dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.3 get-stream: 6.0.1 human-signals: 4.3.1 is-stream: 3.0.0 @@ -7258,7 +7360,7 @@ snapshots: execa@8.0.1: dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.3 get-stream: 8.0.1 human-signals: 5.0.0 is-stream: 3.0.0 @@ -7300,7 +7402,11 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.4.4(picomatch@4.0.2): + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fdir@6.4.5(picomatch@4.0.2): optionalDependencies: picomatch: 4.0.2 @@ -7334,7 +7440,7 @@ snapshots: foreground-child@3.2.1: dependencies: - cross-spawn: 7.0.5 + cross-spawn: 7.0.3 signal-exit: 4.1.0 formdata-node@6.0.3: {} @@ -7425,7 +7531,7 @@ snapshots: dependencies: duplexer: 0.1.2 - happy-dom@17.4.6: + happy-dom@17.5.6: dependencies: webidl-conversions: 7.0.0 whatwg-mimetype: 3.0.0 @@ -7609,7 +7715,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.4.0 + debug: 4.4.1 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color @@ -7633,6 +7739,8 @@ snapshots: js-tokens@9.0.0: {} + js-tokens@9.0.1: {} + jsesc@3.1.0: {} json-parse-even-better-errors@3.0.0: {} @@ -7691,13 +7799,9 @@ snapshots: htmlparser2: 10.0.0 uhyphen: 0.2.0 - linkedom@0.18.9: + linkify-it@5.0.0: dependencies: - css-select: 5.1.0 - cssom: 0.5.0 - html-escaper: 3.0.3 - htmlparser2: 10.0.0 - uhyphen: 0.2.0 + uc.micro: 2.1.0 lint-staged@15.5.1: dependencies: @@ -7792,7 +7896,7 @@ snapshots: magicast@0.3.5: dependencies: '@babel/parser': 7.26.9 - '@babel/types': 7.27.0 + '@babel/types': 7.26.9 source-map-js: 1.2.1 make-dir@4.0.0: @@ -7807,6 +7911,15 @@ snapshots: markdown-it-footnote@4.0.0(patch_hash=5b74a0001a771ff5df790dd36b6883c4794e1f666b231499edb2d65eae8f4930): {} + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + marked@4.3.0: {} marky@1.2.5: {} @@ -7829,6 +7942,8 @@ snapshots: mdn-data@2.12.2: {} + mdurl@2.0.0: {} + merge-anything@5.1.7: dependencies: is-what: 4.1.16 @@ -7885,7 +8000,7 @@ snapshots: mitt@3.0.1: {} - mkdist@2.2.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)): + mkdist@2.2.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)): dependencies: autoprefixer: 10.4.20(postcss@8.5.3) citty: 0.1.6 @@ -7899,9 +8014,9 @@ snapshots: postcss: 8.5.3 postcss-nested: 7.0.2(postcss@8.5.3) semver: 7.7.1 - tinyglobby: 0.2.13 + tinyglobby: 0.2.12 optionalDependencies: - sass: 1.87.0 + sass: 1.89.1 typescript: 5.8.3 vue: 3.5.13(typescript@5.8.3) @@ -8024,13 +8139,6 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 - open@10.1.0: - dependencies: - default-browser: 5.2.1 - define-lazy-prop: 3.0.0 - is-inside-container: 1.0.0 - is-wsl: 3.1.0 - open@10.1.2: dependencies: default-browser: 5.2.1 @@ -8077,16 +8185,16 @@ snapshots: os-shim@0.1.3: {} - oxlint@0.16.8: + oxlint@0.16.12: optionalDependencies: - '@oxlint/darwin-arm64': 0.16.8 - '@oxlint/darwin-x64': 0.16.8 - '@oxlint/linux-arm64-gnu': 0.16.8 - '@oxlint/linux-arm64-musl': 0.16.8 - '@oxlint/linux-x64-gnu': 0.16.8 - '@oxlint/linux-x64-musl': 0.16.8 - '@oxlint/win32-arm64': 0.16.8 - '@oxlint/win32-x64': 0.16.8 + '@oxlint/darwin-arm64': 0.16.12 + '@oxlint/darwin-x64': 0.16.12 + '@oxlint/linux-arm64-gnu': 0.16.12 + '@oxlint/linux-arm64-musl': 0.16.12 + '@oxlint/linux-x64-gnu': 0.16.12 + '@oxlint/linux-x64-musl': 0.16.12 + '@oxlint/win32-arm64': 0.16.12 + '@oxlint/win32-x64': 0.16.12 package-json-from-dist@1.0.0: {} @@ -8419,6 +8527,8 @@ snapshots: end-of-stream: 1.4.4 once: 1.4.0 + punycode.js@2.3.1: {} + pupa@3.1.0: dependencies: escape-goat: 4.0.0 @@ -8583,7 +8693,7 @@ snapshots: safe-stable-stringify@2.5.0: {} - sass@1.87.0: + sass@1.89.1: dependencies: chokidar: 4.0.3 immutable: 5.0.2 @@ -8608,11 +8718,11 @@ snapshots: semver@7.7.1: {} - seroval-plugins@1.2.1(seroval@1.2.1): + seroval-plugins@1.3.2(seroval@1.3.2): dependencies: - seroval: 1.2.1 + seroval: 1.3.2 - seroval@1.2.1: {} + seroval@1.3.2: {} set-value@4.1.0: dependencies: @@ -8706,18 +8816,18 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 - solid-js@1.9.6: + solid-js@1.9.7: dependencies: csstype: 3.1.3 - seroval: 1.2.1 - seroval-plugins: 1.2.1(seroval@1.2.1) + seroval: 1.3.2 + seroval-plugins: 1.3.2(seroval@1.3.2) - solid-refresh@0.6.3(solid-js@1.9.6): + solid-refresh@0.6.3(solid-js@1.9.7): dependencies: '@babel/generator': 7.26.9 '@babel/helper-module-imports': 7.25.9 - '@babel/types': 7.27.0 - solid-js: 1.9.6 + '@babel/types': 7.26.9 + solid-js: 1.9.7 transitivePeerDependencies: - supports-color @@ -8753,6 +8863,8 @@ snapshots: stackback@0.0.2: {} + std-env@3.8.1: {} + std-env@3.9.0: {} stdin-discarder@0.1.0: @@ -8882,16 +8994,21 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.13: + tinyglobby@0.2.12: dependencies: - fdir: 6.4.4(picomatch@4.0.2) + fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 - tinypool@1.0.2: {} + tinyglobby@0.2.14: + dependencies: + fdir: 6.4.5(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.1.0: {} tinyrainbow@2.0.0: {} - tinyspy@3.0.2: {} + tinyspy@4.0.3: {} titleize@3.0.0: {} @@ -8920,7 +9037,7 @@ snapshots: type-fest@3.13.1: {} - type-fest@4.40.1: {} + type-fest@4.41.0: {} typedarray@0.0.6: {} @@ -8949,11 +9066,13 @@ snapshots: ua-parser-js@1.0.40: {} + uc.micro@2.1.0: {} + ufo@1.5.4: {} uhyphen@0.2.0: {} - unbuild@3.5.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)): + unbuild@3.5.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)): dependencies: '@rollup/plugin-alias': 5.1.1(rollup@4.34.9) '@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9) @@ -8969,7 +9088,7 @@ snapshots: hookable: 5.5.3 jiti: 2.4.2 magic-string: 0.30.17 - mkdist: 2.2.0(sass@1.87.0)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) + mkdist: 2.2.0(sass@1.89.1)(typescript@5.8.3)(vue@3.5.13(typescript@5.8.3)) mlly: 1.7.4 pathe: 2.0.3 pkg-types: 2.1.0 @@ -8977,7 +9096,7 @@ snapshots: rollup: 4.34.9 rollup-plugin-dts: 6.1.1(rollup@4.34.9)(typescript@5.8.3) scule: 1.3.0 - tinyglobby: 0.2.13 + tinyglobby: 0.2.12 untyped: 2.0.0 optionalDependencies: typescript: 5.8.3 @@ -9038,9 +9157,9 @@ snapshots: universalify@2.0.0: {} - unocss@66.0.0(postcss@8.5.3)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)): + unocss@66.0.0(postcss@8.5.3)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)): dependencies: - '@unocss/astro': 66.0.0(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + '@unocss/astro': 66.0.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) '@unocss/cli': 66.0.0 '@unocss/core': 66.0.0 '@unocss/postcss': 66.0.0(postcss@8.5.3) @@ -9057,9 +9176,9 @@ snapshots: '@unocss/transformer-compile-class': 66.0.0 '@unocss/transformer-directives': 66.0.0 '@unocss/transformer-variant-group': 66.0.0 - '@unocss/vite': 66.0.0(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) + '@unocss/vite': 66.0.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0))(vue@3.5.13(typescript@5.8.3)) optionalDependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - postcss - supports-color @@ -9122,13 +9241,13 @@ snapshots: '@types/unist': 3.0.2 vfile-message: 4.0.2 - vite-node@3.1.2(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0): + vite-node@3.2.0(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0): dependencies: cac: 6.7.14 - debug: 4.4.0 - es-module-lexer: 1.6.0 + debug: 4.4.1 + es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) transitivePeerDependencies: - '@types/node' - jiti @@ -9143,20 +9262,20 @@ snapshots: - tsx - yaml - vite-plugin-solid@2.11.6(solid-js@1.9.6)(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)): + vite-plugin-solid@2.11.6(solid-js@1.9.7)(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)): dependencies: '@babel/core': 7.26.9 '@types/babel__core': 7.20.5 babel-preset-solid: 1.9.5(@babel/core@7.26.9) merge-anything: 5.1.7 - solid-js: 1.9.6 - solid-refresh: 0.6.3(solid-js@1.9.6) - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) - vitefu: 1.0.6(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) + solid-js: 1.9.7 + solid-refresh: 0.6.3(solid-js@1.9.7) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) + vitefu: 1.0.6(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) transitivePeerDependencies: - supports-color - vite@5.4.19(@types/node@20.17.30)(sass@1.87.0): + vite@5.4.19(@types/node@20.17.30)(sass@1.89.1): dependencies: esbuild: 0.21.5 postcss: 8.5.3 @@ -9164,45 +9283,47 @@ snapshots: optionalDependencies: '@types/node': 20.17.30 fsevents: 2.3.3 - sass: 1.87.0 + sass: 1.89.1 - vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0): + vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0): dependencies: esbuild: 0.25.0 - fdir: 6.4.4(picomatch@4.0.2) + fdir: 6.4.5(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.34.9 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 20.17.30 fsevents: 2.3.3 jiti: 2.4.2 - sass: 1.87.0 + sass: 1.89.1 tsx: 4.19.4 yaml: 2.7.0 - vitefu@1.0.6(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)): + vitefu@1.0.6(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)): optionalDependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) - vitepress-knowledge@0.4.1(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.87.0)(search-insights@2.15.0)(typescript@5.8.3)): + vitepress-knowledge@0.4.1(vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(search-insights@2.15.0)(typescript@5.8.3)): dependencies: - linkedom: 0.18.9 + linkedom: 0.18.10 node-html-markdown: 1.3.0 picocolors: 1.1.1 - vitepress: 1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.87.0)(search-insights@2.15.0)(typescript@5.8.3) + vitepress: 1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(search-insights@2.15.0)(typescript@5.8.3) yaml: 2.7.0 - vitepress-plugin-group-icons@1.5.2: + vitepress-plugin-group-icons@1.5.5(markdown-it@14.1.0)(vite@5.4.19(@types/node@20.17.30)(sass@1.89.1)): dependencies: '@iconify-json/logos': 1.2.4 '@iconify-json/vscode-icons': 1.2.19 '@iconify/utils': 2.3.0 + markdown-it: 14.1.0 + vite: 5.4.19(@types/node@20.17.30)(sass@1.89.1) transitivePeerDependencies: - supports-color - vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.87.0)(search-insights@2.15.0)(typescript@5.8.3): + vitepress@1.6.3(@algolia/client-search@5.20.3)(@types/node@20.17.30)(@types/react@18.3.12)(postcss@8.5.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.89.1)(search-insights@2.15.0)(typescript@5.8.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.20.3)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(search-insights@2.15.0) @@ -9211,7 +9332,7 @@ snapshots: '@shikijs/transformers': 2.5.0 '@shikijs/types': 2.5.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 5.2.3(vite@5.4.19(@types/node@20.17.30)(sass@1.87.0))(vue@3.5.13(typescript@5.8.3)) + '@vitejs/plugin-vue': 5.2.3(vite@5.4.19(@types/node@20.17.30)(sass@1.89.1))(vue@3.5.13(typescript@5.8.3)) '@vue/devtools-api': 7.7.2 '@vue/shared': 3.5.13 '@vueuse/core': 12.7.0(typescript@5.8.3) @@ -9220,7 +9341,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.1.2 shiki: 2.5.0 - vite: 5.4.19(@types/node@20.17.30)(sass@1.87.0) + vite: 5.4.19(@types/node@20.17.30)(sass@1.89.1) vue: 3.5.13(typescript@5.8.3) optionalDependencies: postcss: 8.5.3 @@ -9251,42 +9372,44 @@ snapshots: - typescript - universal-cookie - vitest-mock-extended@3.1.0(typescript@5.8.3)(vitest@3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)): + vitest-mock-extended@3.1.0(typescript@5.8.3)(vitest@3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)): dependencies: ts-essentials: 10.0.1(typescript@5.8.3) typescript: 5.8.3 - vitest: 3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vitest: 3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) - vitest-plugin-random-seed@1.1.1(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)): + vitest-plugin-random-seed@1.1.1(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)): dependencies: - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) - vitest@3.1.2(@types/node@20.17.30)(happy-dom@17.4.6)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0): + vitest@3.2.0(@types/node@20.17.30)(happy-dom@17.5.6)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0): dependencies: - '@vitest/expect': 3.1.2 - '@vitest/mocker': 3.1.2(vite@6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0)) - '@vitest/pretty-format': 3.1.2 - '@vitest/runner': 3.1.2 - '@vitest/snapshot': 3.1.2 - '@vitest/spy': 3.1.2 - '@vitest/utils': 3.1.2 + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.0 + '@vitest/mocker': 3.2.0(vite@6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0)) + '@vitest/pretty-format': 3.2.0 + '@vitest/runner': 3.2.0 + '@vitest/snapshot': 3.2.0 + '@vitest/spy': 3.2.0 + '@vitest/utils': 3.2.0 chai: 5.2.0 - debug: 4.4.0 + debug: 4.4.1 expect-type: 1.2.1 magic-string: 0.30.17 pathe: 2.0.3 + picomatch: 4.0.2 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 - tinyglobby: 0.2.13 - tinypool: 1.0.2 + tinyglobby: 0.2.14 + tinypool: 1.1.0 tinyrainbow: 2.0.0 - vite: 6.3.4(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) - vite-node: 3.1.2(@types/node@20.17.30)(jiti@2.4.2)(sass@1.87.0)(tsx@4.19.4)(yaml@2.7.0) + vite: 6.3.5(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) + vite-node: 3.2.0(@types/node@20.17.30)(jiti@2.4.2)(sass@1.89.1)(tsx@4.19.4)(yaml@2.7.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 20.17.30 - happy-dom: 17.4.6 + happy-dom: 17.5.6 transitivePeerDependencies: - jiti - less