diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 336d925b..e3faddaa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,13 @@ jobs: - name: test-e2e run: pnpm test-e2e --retry 2 + - name: vitest 3.2 + run: node ./scripts/lower-vitest-version.js + - run: pnpm install --no-frozen-lockfile + + - name: test-e2e-legacy + run: pnpm test-e2e --retry 2 + - uses: actions/upload-artifact@v4 if: always() with: diff --git a/.vscode/launch.json b/.vscode/launch.json index ec8d9442..2fb99341 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -15,6 +15,16 @@ ], "outFiles": ["${workspaceFolder}/dist/**/*.js"] }, + { + "name": "Run Extension Basic V4 Sample", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}/samples/basic-v4" + ], + "outFiles": ["${workspaceFolder}/dist/**/*.js"] + }, { "name": "Run Extension Browser Sample", "type": "extensionHost", diff --git a/eslint.config.mjs b/eslint.config.mjs index fccad125..455246e5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -52,17 +52,6 @@ export default antfu( 'antfu/no-import-dist': 'off', }, }, - { - files: [`packages/${GLOB_SRC}`], - rules: { - 'no-restricted-imports': [ - 'error', - { - paths: ['vitest', 'path'], - }, - ], - }, - }, { // these files define vitest as peer dependency files: [`packages/{coverage-*,ui,browser,web-worker}/${GLOB_SRC}`], diff --git a/package.json b/package.json index cd0c57c2..21a116e5 100644 --- a/package.json +++ b/package.json @@ -313,6 +313,26 @@ "which": "^4.0.0", "ws": "^8.16.0" }, + "pnpm": { + "overrides": { + "@vitest/browser": "^4.0.0-beta.8", + "@vitest/coverage": "^4.0.0-beta.8", + "@vitest/runner": "^4.0.0-beta.8", + "vitest": "^4.0.0-beta.8", + "vitest-vscode-extension>@vitest/browser": "^3.2.0", + "vitest-vscode-extension>@vitest/coverage": "^3.2.0", + "vitest-vscode-extension>@vitest/runner": "^3.2.0", + "vitest-vscode-extension>vitest": "^3.2.0", + "vitest-vscode-shared>@vitest/browser": "^3.2.0", + "vitest-vscode-shared>@vitest/coverage": "^3.2.0", + "vitest-vscode-shared>@vitest/runner": "^3.2.0", + "vitest-vscode-shared>vitest": "^3.2.0", + "vitest-vscode-worker-legacy>@vitest/browser": "^3.2.0", + "vitest-vscode-worker-legacy>@vitest/coverage": "^3.2.0", + "vitest-vscode-worker-legacy>@vitest/runner": "^3.2.0", + "vitest-vscode-worker-legacy>vitest": "^3.2.0" + } + }, "lint-staged": { "*.{js,ts,tsx,vue,md}": [ "eslint --fix" diff --git a/packages/extension/package.json b/packages/extension/package.json new file mode 100644 index 00000000..4e9847d5 --- /dev/null +++ b/packages/extension/package.json @@ -0,0 +1,9 @@ +{ + "name": "vitest-vscode-extension", + "version": "0.0.0", + "private": true, + "dependencies": { + "vitest": "$vitest", + "vitest-vscode-shared": "workspace:*" + } +} diff --git a/src/api.ts b/packages/extension/src/api.ts similarity index 88% rename from src/api.ts rename to packages/extension/src/api.ts index f901fc77..153ec046 100644 --- a/src/api.ts +++ b/packages/extension/src/api.ts @@ -1,8 +1,9 @@ import type { VitestPackage } from './api/pkg' -import type { ExtensionWorkerEvents, SerializedTestSpecification, VitestRPC } from './api/rpc' +import type { ExtensionWorkerEvents, VitestExtensionRPC } from './api/rpc' import type { ExtensionWorkerProcess } from './api/types' import { dirname, isAbsolute } from 'node:path' import { normalize, relative } from 'pathe' +import { createQueuedHandler, type ExtensionTestSpecification } from 'vitest-vscode-shared' import * as vscode from 'vscode' import { createVitestProcess } from './api/child_process' import { createVitestTerminalProcess } from './api/terminal' @@ -110,11 +111,11 @@ export class VitestFolderAPI { return this.pkg } - async runFiles(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) { + async runFiles(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) { await this.meta.rpc.runTests(normalizeSpecs(specs), testNamePatern) } - async updateSnapshots(specs?: SerializedTestSpecification[] | string[], testNamePatern?: string) { + async updateSnapshots(specs?: ExtensionTestSpecification[] | string[], testNamePatern?: string) { await this.meta.rpc.updateSnapshots(normalizeSpecs(specs), testNamePatern) } @@ -197,7 +198,7 @@ export class VitestFolderAPI { await this.meta.rpc.disableCoverage() } - async watchTests(files?: SerializedTestSpecification[] | string[], testNamePattern?: string) { + async watchTests(files?: ExtensionTestSpecification[] | string[], testNamePattern?: string) { await this.meta.rpc.watchTests(normalizeSpecs(files), testNamePattern) } @@ -207,10 +208,9 @@ export class VitestFolderAPI { onConsoleLog = this.createHandler('onConsoleLog') onTaskUpdate = this.createHandler('onTaskUpdate') - onFinished = this.createHandler('onFinished') + onTestRunEnd = this.createHandler('onTestRunEnd') + onTestRunStart = this.createHandler('onTestRunStart') onCollected = this.createHandler('onCollected') - onWatcherStart = this.createHandler('onWatcherStart') - onWatcherRerun = this.createHandler('onWatcherRerun') clearListeners(name?: Exclude) { if (name) @@ -230,28 +230,6 @@ export class VitestFolderAPI { } } -function createQueuedHandler(resolver: (value: T[]) => Promise) { - const cached = new Set() - let promise: Promise | null = null - let timer: NodeJS.Timeout | null = null - return (value: T) => { - cached.add(value) - if (timer) { - clearTimeout(timer) - } - timer = setTimeout(() => { - if (promise) { - return - } - const values = Array.from(cached) - cached.clear() - promise = resolver(values).finally(() => { - promise = null - }) - }, 50) - } -} - export async function resolveVitestAPI(workspaceConfigs: VitestPackage[], configs: VitestPackage[]) { const usedConfigs = new Set() const workspacePromises = workspaceConfigs.map(pkg => createVitestFolderAPI(usedConfigs, pkg)) @@ -402,7 +380,7 @@ async function createVitestFolderAPI(usedConfigs: Set, pkg: VitestPackag } export interface ResolvedMeta { - rpc: VitestRPC + rpc: VitestExtensionRPC process: ExtensionWorkerProcess workspaceSource: string | false pkg: VitestPackage @@ -411,16 +389,15 @@ export interface ResolvedMeta { onStdout: (listener: (log: string) => void) => void onConsoleLog: (listener: ExtensionWorkerEvents['onConsoleLog']) => void onTaskUpdate: (listener: ExtensionWorkerEvents['onTaskUpdate']) => void - onFinished: (listener: ExtensionWorkerEvents['onFinished']) => void + onTestRunEnd: (listener: ExtensionWorkerEvents['onTestRunEnd']) => void + onTestRunStart: (listener: ExtensionWorkerEvents['onTestRunStart']) => void onCollected: (listener: ExtensionWorkerEvents['onCollected']) => void - onWatcherStart: (listener: ExtensionWorkerEvents['onWatcherStart']) => void - onWatcherRerun: (listener: ExtensionWorkerEvents['onWatcherRerun']) => void clearListeners: () => void removeListener: (name: string, listener: any) => void } } -function normalizeSpecs(specs?: string[] | SerializedTestSpecification[]) { +function normalizeSpecs(specs?: string[] | ExtensionTestSpecification[]) { if (!specs) { return specs } @@ -428,6 +405,6 @@ function normalizeSpecs(specs?: string[] | SerializedTestSpecification[]) { if (typeof spec === 'string') { return normalize(spec) } - return [spec[0], normalize(spec[1])] as SerializedTestSpecification - }) as string[] | SerializedTestSpecification[] + return [spec[0], normalize(spec[1])] as ExtensionTestSpecification + }) as string[] | ExtensionTestSpecification[] } diff --git a/src/api/child_process.ts b/packages/extension/src/api/child_process.ts similarity index 100% rename from src/api/child_process.ts rename to packages/extension/src/api/child_process.ts diff --git a/src/api/pkg.ts b/packages/extension/src/api/pkg.ts similarity index 100% rename from src/api/pkg.ts rename to packages/extension/src/api/pkg.ts diff --git a/src/api/resolve.ts b/packages/extension/src/api/resolve.ts similarity index 97% rename from src/api/resolve.ts rename to packages/extension/src/api/resolve.ts index 6312c6de..7a17f8de 100644 --- a/src/api/resolve.ts +++ b/packages/extension/src/api/resolve.ts @@ -1,8 +1,8 @@ import type * as vscode from 'vscode' import { findUpSync } from 'find-up' import { dirname, resolve } from 'pathe' +import { normalizeDriveLetter } from 'vitest-vscode-shared' import { getConfig } from '../config' -import { normalizeDriveLetter } from '../worker/utils' const _require = require diff --git a/packages/extension/src/api/rpc.ts b/packages/extension/src/api/rpc.ts new file mode 100644 index 00000000..b2a23b9d --- /dev/null +++ b/packages/extension/src/api/rpc.ts @@ -0,0 +1,94 @@ +import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared' +import { stripVTControlCharacters } from 'node:util' +import v8 from 'node:v8' +import { createBirpc } from 'birpc' +import { log } from '../log' + +export type { + ExtensionWorkerEvents, + ExtensionWorkerTransport, + VitestExtensionRPC, + VitestWorkerRPC, +} from 'vitest-vscode-shared' + +function createHandler any>() { + const handlers: T[] = [] + return { + handlers, + register: (listener: any) => handlers.push(listener), + trigger: (...data: any) => handlers.forEach(handler => handler(...data)), + clear: () => handlers.length = 0, + remove: (listener: T) => { + const index = handlers.indexOf(listener) + if (index !== -1) + handlers.splice(index, 1) + }, + } +} + +export function createRpcOptions() { + const handlers = { + onConsoleLog: createHandler(), + onTaskUpdate: createHandler(), + onCollected: createHandler(), + onTestRunStart: createHandler(), + onTestRunEnd: createHandler(), + } + + const events: Omit = { + onConsoleLog: handlers.onConsoleLog.trigger, + onTestRunEnd: handlers.onTestRunEnd.trigger, + onTaskUpdate: handlers.onTaskUpdate.trigger, + onCollected: handlers.onCollected.trigger, + onTestRunStart: handlers.onTestRunStart.trigger, + onProcessLog(type, message) { + log.worker(type === 'stderr' ? 'error' : 'info', stripVTControlCharacters(message)) + }, + } + + return { + events, + handlers: { + onConsoleLog: handlers.onConsoleLog.register, + onTaskUpdate: handlers.onTaskUpdate.register, + onTestRunEnd: handlers.onTestRunEnd.register, + onCollected: handlers.onCollected.register, + onTestRunStart: handlers.onTestRunStart.register, + removeListener(name: string, listener: any) { + handlers[name as 'onCollected']?.remove(listener) + }, + clearListeners() { + for (const name in handlers) + handlers[name as 'onCollected']?.clear() + }, + }, + } +} + +export function createVitestRpc(options: { + on: (listener: (message: any) => void) => void + send: (message: any) => void +}) { + const { events, handlers } = createRpcOptions() + + const api = createBirpc( + events, + { + timeout: -1, + bind: 'functions', + on(listener) { + options.on(listener) + }, + post(message) { + options.send(message) + }, + serialize: v8.serialize, + deserialize: v => v8.deserialize(Buffer.from(v) as any), + }, + ) + + return { + api, + handlers, + } +} diff --git a/src/api/terminal.ts b/packages/extension/src/api/terminal.ts similarity index 100% rename from src/api/terminal.ts rename to packages/extension/src/api/terminal.ts diff --git a/src/api/types.ts b/packages/extension/src/api/types.ts similarity index 100% rename from src/api/types.ts rename to packages/extension/src/api/types.ts diff --git a/src/api/ws.ts b/packages/extension/src/api/ws.ts similarity index 94% rename from src/api/ws.ts rename to packages/extension/src/api/ws.ts index ad348976..ee423c08 100644 --- a/src/api/ws.ts +++ b/packages/extension/src/api/ws.ts @@ -1,10 +1,11 @@ +import type { WorkerEvent, WorkerRunnerOptions } from 'vitest-vscode-shared' import type { WebSocket, WebSocketServer } from 'ws' import type { ResolvedMeta } from '../api' -import type { WorkerEvent, WorkerRunnerOptions } from '../worker/types' import type { VitestPackage } from './pkg' import { pathToFileURL } from 'node:url' import { gte } from 'semver' import { getConfig } from '../config' +import { finalCoverageFileName, setupFilePath } from '../constants' import { log } from '../log' import { createVitestRpc } from './rpc' @@ -131,6 +132,8 @@ export function onWsConnection( pnpLoader: pnpLoader && gte(process.version, '18.19.0') ? pathToFileURL(pnpLoader).toString() : undefined, + setupFilePath, + finalCoverageFileName, }, debug, astCollect: getConfig(pkg.folder).experimentalStaticAstCollect, diff --git a/src/config.ts b/packages/extension/src/config.ts similarity index 100% rename from src/config.ts rename to packages/extension/src/config.ts diff --git a/src/constants.ts b/packages/extension/src/constants.ts similarity index 100% rename from src/constants.ts rename to packages/extension/src/constants.ts diff --git a/src/coverage.ts b/packages/extension/src/coverage.ts similarity index 100% rename from src/coverage.ts rename to packages/extension/src/coverage.ts diff --git a/src/debug.ts b/packages/extension/src/debug.ts similarity index 100% rename from src/debug.ts rename to packages/extension/src/debug.ts diff --git a/src/diagnostic.ts b/packages/extension/src/diagnostic.ts similarity index 100% rename from src/diagnostic.ts rename to packages/extension/src/diagnostic.ts diff --git a/src/extension.ts b/packages/extension/src/extension.ts similarity index 100% rename from src/extension.ts rename to packages/extension/src/extension.ts diff --git a/src/log.ts b/packages/extension/src/log.ts similarity index 100% rename from src/log.ts rename to packages/extension/src/log.ts diff --git a/src/polyfills.ts b/packages/extension/src/polyfills.ts similarity index 100% rename from src/polyfills.ts rename to packages/extension/src/polyfills.ts diff --git a/src/runner.ts b/packages/extension/src/runner.ts similarity index 94% rename from src/runner.ts rename to packages/extension/src/runner.ts index 648246ef..c388efb5 100644 --- a/src/runner.ts +++ b/packages/extension/src/runner.ts @@ -1,6 +1,5 @@ -import type { ParsedStack, TaskResult, TestError } from 'vitest' +import type { ParsedStack, RunnerTaskResult, TestError } from 'vitest' import type { VitestFolderAPI } from './api' -import type { SerializedTestSpecification } from './api/rpc' import type { ExtensionDiagnostic } from './diagnostic' import type { TestTree } from './testTree' import { rm } from 'node:fs/promises' @@ -8,12 +7,12 @@ import path from 'node:path' import { inspect, stripVTControlCharacters } from 'node:util' import { getTasks } from '@vitest/runner/utils' import { basename, normalize, relative } from 'pathe' +import { type ExtensionTestSpecification, normalizeDriveLetter } from 'vitest-vscode-shared' import * as vscode from 'vscode' import { coverageContext, readCoverageReport } from './coverage' import { log } from './log' import { getTestData, TestCase, TestFile, TestFolder } from './testTreeData' import { showVitestError } from './utils' -import { normalizeDriveLetter } from './worker/utils' export class TestRunner extends vscode.Disposable { private continuousRequests = new Set() @@ -53,7 +52,11 @@ export class TestRunner extends vscode.Disposable { } }) - api.onWatcherRerun((files, _trigger, collecting) => { + api.onTestRunStart((files, collecting) => { + if (!files.length) { + return + } + if (collecting) { log.verbose?.('Not starting the runner because tests are being collected for', ...files.map(f => this.relative(f))) } @@ -85,16 +88,12 @@ export class TestRunner extends vscode.Disposable { }) }) - api.onCollected((files, collecting) => { - if (!files || !files.length) { - log.verbose?.('No files to collect') - return - } - files.forEach(file => this.tree.collectFile(this.api, file)) + api.onCollected((file, collecting) => { + this.tree.collectFile(this.api, file) if (collecting) return - getTasks(files).forEach((task) => { + getTasks(file).forEach((task) => { const test = this.tree.getTestItemByTask(task) if (!test) { log.error(`Test data not found for "${task.name}"`) @@ -125,7 +124,7 @@ export class TestRunner extends vscode.Disposable { }) }) - api.onFinished(async (files = [], unhandledError, collecting) => { + api.onTestRunEnd(async (files, unhandledError, collecting) => { const testRun = this.testRun if (!testRun) { @@ -184,7 +183,7 @@ export class TestRunner extends vscode.Disposable { this.disposables.push( token.onCancellationRequested(() => { - log.verbose?.('Continuous test run for', join(request.include), 'was cancelled') + log.verbose?.('Continuous test run for', labelTestItems(request.include), 'was cancelled') this.continuousRequests.delete(request) if (!this.continuousRequests.size) { @@ -233,7 +232,7 @@ export class TestRunner extends vscode.Disposable { this.disposables.push( token.onCancellationRequested(() => { - log.verbose?.('Coverage for', join(request.include), 'was manually stopped') + log.verbose?.('Coverage for', labelTestItems(request.include), 'was manually stopped') this.api.disableCoverage() }), ) @@ -286,12 +285,12 @@ export class TestRunner extends vscode.Disposable { this.nonContinuousRequest = undefined this.endTestRun() }) - log.verbose?.('Test run was cancelled manually for', join(request.include)) + log.verbose?.('Test run was cancelled manually for', labelTestItems(request.include)) } }), ) - const runTests = (files?: SerializedTestSpecification[] | string[], testNamePatern?: string) => + const runTests = (files?: ExtensionTestSpecification[] | string[], testNamePatern?: string) => 'updateSnapshots' in request ? this.api.updateSnapshots(files, testNamePatern) : this.api.runFiles(files, testNamePatern) @@ -396,6 +395,11 @@ export class TestRunner extends vscode.Disposable { private async startTestRun(files: string[], primaryRequest?: vscode.TestRunRequest) { const request = primaryRequest || this.nonContinuousRequest || this.createContinuousRequest() + if (!files.length) { + log.verbose?.('Started an empty test run. This should not happen...') + return + } + if (!request) { log.verbose?.('No test run request found for', ...files.map(f => this.relative(f))) return @@ -500,7 +504,7 @@ export class TestRunner extends vscode.Disposable { private markTestCase( testRun: vscode.TestRun, test: vscode.TestItem, - result: TaskResult, + result: RunnerTaskResult, ) { switch (result.state) { case 'fail': { @@ -542,7 +546,7 @@ export class TestRunner extends vscode.Disposable { // we only change the state of test cases to keep the correct test count // ignoring test files, test folders and suites - these only report syntax errors - private markNonTestCase(test: vscode.TestItem, result?: TaskResult) { + private markNonTestCase(test: vscode.TestItem, result?: RunnerTaskResult) { if (!result) { log.verbose?.(`No task result for "${test.label}", ignoring`) return @@ -560,7 +564,7 @@ export class TestRunner extends vscode.Disposable { test.error = errors.join('\n') } - private markResult(testRun: vscode.TestRun, test: vscode.TestItem, result?: TaskResult) { + private markResult(testRun: vscode.TestRun, test: vscode.TestItem, result?: RunnerTaskResult) { const isTestCase = getTestData(test) instanceof TestCase if (!isTestCase) { @@ -577,7 +581,7 @@ export class TestRunner extends vscode.Disposable { this.markTestCase(testRun, test, result) } - private relative(file: string | SerializedTestSpecification) { + private relative(file: string | ExtensionTestSpecification) { return relative(this.api.workspaceFolder.uri.fsPath, typeof file === 'string' ? file : file[1]) } } @@ -713,7 +717,7 @@ function setMessageStackFramesFromErrorStacks(testMessage: vscode.TestMessage, s (testMessage as any).stackTrace = frames } -function getTestFiles(tests: readonly vscode.TestItem[]): string[] | SerializedTestSpecification[] { +function getTestFiles(tests: readonly vscode.TestItem[]): string[] | ExtensionTestSpecification[] { // if there is a folder, we can't limit the tests to a specific project const hasFolder = tests.some(test => getTestData(test) instanceof TestFolder) if (hasFolder) { @@ -727,7 +731,7 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | SerializedT }).filter(Boolean) as string[]), ) } - const testSpecs: SerializedTestSpecification[] = [] + const testSpecs: ExtensionTestSpecification[] = [] const testFiles = new Set() for (const test of tests) { const fsPath = test.uri!.fsPath @@ -742,7 +746,7 @@ function getTestFiles(tests: readonly vscode.TestItem[]): string[] | SerializedT continue } testFiles.add(key) - testSpecs.push([{ name: project }, fsPath]) + testSpecs.push([project, fsPath]) } return testSpecs } @@ -764,7 +768,7 @@ function formatTestOutput(output: string) { return output.replace(/(?' return items.map(p => `"${p.label}"`).join(', ') diff --git a/src/tagsManager.ts b/packages/extension/src/tagsManager.ts similarity index 100% rename from src/tagsManager.ts rename to packages/extension/src/tagsManager.ts diff --git a/src/testTree.ts b/packages/extension/src/testTree.ts similarity index 100% rename from src/testTree.ts rename to packages/extension/src/testTree.ts diff --git a/src/testTreeData.ts b/packages/extension/src/testTreeData.ts similarity index 100% rename from src/testTreeData.ts rename to packages/extension/src/testTreeData.ts diff --git a/src/utils.ts b/packages/extension/src/utils.ts similarity index 100% rename from src/utils.ts rename to packages/extension/src/utils.ts diff --git a/src/watcher.ts b/packages/extension/src/watcher.ts similarity index 100% rename from src/watcher.ts rename to packages/extension/src/watcher.ts diff --git a/src/worker/index.ts b/packages/extension/src/worker/index.ts similarity index 51% rename from src/worker/index.ts rename to packages/extension/src/worker/index.ts index 893f1b17..ab52f0b0 100644 --- a/src/worker/index.ts +++ b/packages/extension/src/worker/index.ts @@ -1,10 +1,9 @@ -import type { WorkerRunnerOptions } from './types' +import type { WorkerRunnerOptions } from 'vitest-vscode-shared' +import { join } from 'node:path' +import { pathToFileURL } from 'node:url' import v8 from 'node:v8' +import { createWorkerRPC, normalizeDriveLetter, WorkerWSEventEmitter } from 'vitest-vscode-shared' import { WebSocket } from 'ws' -import { WorkerWSEventEmitter } from './emitter' -import { initVitest } from './init' -import { createWorkerRPC } from './rpc' -import { ExtensionWorker } from './worker' // this is the file that will be executed with "node " @@ -22,20 +21,25 @@ emitter.on('message', async function onMessage(message: any) { const data = message as WorkerRunnerOptions try { - const { reporter, vitest, configs, workspaceSource } = await initVitest( - data.meta, - data.debug - ? { - disableConsoleIntercept: true, - fileParallelism: false, - testTimeout: 0, - hookTimeout: 0, - } - : {}, + const vitestModule = await import( + pathToFileURL(normalizeDriveLetter(data.meta.vitestNodePath)).toString() + ) as typeof import('vitest/node') + + const isOld = Number(vitestModule.version[0]) < 4 + const workerName = isOld ? './workerLegacy.js' : './workerNew.js' + const workerPath = pathToFileURL(join(__dirname, workerName)) + const initModule = await import(workerPath.toString()) + + const { createWorker, reporter, configs, workspaceSource } = await initModule.initVitest( + vitestModule, + data, + emitter, ) + const worker = createWorker() + const rpc = createWorkerRPC( - new ExtensionWorker(vitest, data.debug, data.astCollect, emitter), + worker, { on(listener) { emitter.on('message', listener) @@ -47,6 +51,7 @@ emitter.on('message', async function onMessage(message: any) { deserialize: v => v8.deserialize(Buffer.from(v) as any), }, ) + worker.initRpc(rpc) reporter.initRpc(rpc) emitter.ready(configs, workspaceSource) } diff --git a/src/worker/setupFile.ts b/packages/extension/src/worker/setupFile.ts similarity index 81% rename from src/worker/setupFile.ts rename to packages/extension/src/worker/setupFile.ts index 6b776646..d458895e 100644 --- a/src/worker/setupFile.ts +++ b/packages/extension/src/worker/setupFile.ts @@ -1,6 +1,5 @@ import type { WorkerGlobalState } from 'vitest' import { inject } from 'vitest' -import { assert } from './utils' const { watchEveryFile, continuousFiles, rerunTriggered } = inject('__vscode') // @ts-expect-error injected global @@ -26,3 +25,9 @@ function testFileWatched() { return false }) } + +function assert(condition: unknown, message: string | (() => string)): asserts condition { + if (!condition) { + throw new Error(typeof message === 'string' ? message : message()) + } +} diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 00000000..344b08f7 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,13 @@ +{ + "name": "vitest-vscode-shared", + "type": "module", + "version": "0.0.0", + "private": true, + "exports": { + ".": "./src/index.ts" + }, + "devDependencies": { + "birpc": "2.4.0", + "vitest": "$vitest" + } +} diff --git a/src/worker/emitter.ts b/packages/shared/src/emitter.ts similarity index 95% rename from src/worker/emitter.ts rename to packages/shared/src/emitter.ts index fc767996..1f06a010 100644 --- a/src/worker/emitter.ts +++ b/packages/shared/src/emitter.ts @@ -1,5 +1,5 @@ +import type { WorkerEvent } from 'vitest-vscode-shared' import type WebSocket from 'ws' -import type { WorkerEvent } from './types' abstract class WorkerEventEmitter { abstract name: string diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 00000000..5b8f31e9 --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,92 @@ +import type { BirpcReturn } from 'birpc' +import type { + RunnerTaskResultPack, + RunnerTestFile, + UserConsoleLog, +} from 'vitest' + +export { WorkerWSEventEmitter } from './emitter' +export { createWorkerRPC } from './rpc' +export { assert, createQueuedHandler, limitConcurrency, normalizeDriveLetter } from './utils' + +export type ExtensionTestSpecification = [ + project: string, + file: string, +] + +export interface ExtensionWorkerTransport { + getFiles: () => Promise + collectTests: (testFile: ExtensionTestSpecification[]) => Promise + cancelRun: () => Promise + // accepts files with the project or folders (project doesn't matter for them) + runTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise + updateSnapshots: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => Promise + + watchTests: (filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string) => void + unwatchTests: () => void + + invalidateIstanbulTestModules: (modules: string[] | null) => Promise + enableCoverage: () => void + disableCoverage: () => void + waitForCoverageReport: () => Promise + close: () => void + + onFilesCreated: (files: string[]) => void + onFilesChanged: (files: string[]) => void + + initRpc: (rpc: VitestWorkerRPC) => void +} + +export interface ExtensionWorkerEvents { + onConsoleLog: (log: UserConsoleLog) => void + onTaskUpdate: (task: RunnerTaskResultPack[]) => void + onTestRunEnd: (files: RunnerTestFile[], unhandledError: string, collecting?: boolean) => void + onCollected: (file: RunnerTestFile, collecting?: boolean) => void + onTestRunStart: (files: string[], collecting?: boolean) => void + + onProcessLog: (type: 'stdout' | 'stderr', log: string) => void +} + +export type VitestExtensionRPC = BirpcReturn +export type VitestWorkerRPC = BirpcReturn + +export interface WorkerInitMetadata { + vitestNodePath: string + id: string + cwd: string + arguments?: string + configFile?: string + workspaceFile?: string + env: Record | undefined + shellType: 'terminal' | 'child_process' + hasShellIntegration: boolean + pnpApi?: string + pnpLoader?: string + setupFilePath: string + finalCoverageFileName: string +} + +export interface WorkerRunnerOptions { + type: 'init' + meta: WorkerInitMetadata + debug: boolean + astCollect: boolean +} + +export interface EventReady { + type: 'ready' + configs: string[] + workspaceSource: string | false +} + +export interface EventDebug { + type: 'debug' + args: string[] +} + +export interface EventError { + type: 'error' + error: string +} + +export type WorkerEvent = EventReady | EventDebug | EventError diff --git a/src/worker/rpc.ts b/packages/shared/src/rpc.ts similarity index 64% rename from src/worker/rpc.ts rename to packages/shared/src/rpc.ts index 8f55147b..930b8774 100644 --- a/src/worker/rpc.ts +++ b/packages/shared/src/rpc.ts @@ -1,19 +1,17 @@ import type { ChannelOptions } from 'birpc' -import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from '../api/rpc' -import type { ExtensionWorker } from './worker' +import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared' import { createBirpc } from 'birpc' -export function createWorkerRPC(vitest: ExtensionWorker, channel: ChannelOptions) { +export function createWorkerRPC(vitest: ExtensionWorkerTransport, channel: ChannelOptions) { const rpc = createBirpc(vitest, { timeout: -1, bind: 'functions', eventNames: [ 'onConsoleLog', 'onTaskUpdate', - 'onFinished', 'onCollected', - 'onWatcherRerun', - 'onWatcherStart', + 'onTestRunStart', + 'onTestRunEnd', ], ...channel, }) diff --git a/src/worker/utils.ts b/packages/shared/src/utils.ts similarity index 79% rename from src/worker/utils.ts rename to packages/shared/src/utils.ts index 6eb70a6b..3b954c57 100644 --- a/src/worker/utils.ts +++ b/packages/shared/src/utils.ts @@ -62,18 +62,32 @@ export function assert(condition: unknown, message: string | (() => string)): as } } +const driveLetter = process.platform === 'win32' ? __dirname[0] : null + export function normalizeDriveLetter(path: string) { - if (process.platform !== 'win32') + if (!driveLetter) return path - const currentDriveLetter = __dirname[0] - const letterCase = currentDriveLetter === currentDriveLetter.toUpperCase() - ? 'uppercase' - : 'lowercase' - const targetDriveLetter = path[0] - if (letterCase === 'lowercase') { - const driveLetter = targetDriveLetter.toLowerCase() - return driveLetter + path.slice(1) - } - const driveLetter = targetDriveLetter.toUpperCase() return driveLetter + path.slice(1) } + +export function createQueuedHandler(resolver: (value: T[]) => Promise) { + const cached = new Set() + let promise: Promise | null = null + let timer: NodeJS.Timeout | null = null + return (value: T) => { + cached.add(value) + if (timer) { + clearTimeout(timer) + } + timer = setTimeout(() => { + if (promise) { + return + } + const values = Array.from(cached) + cached.clear() + promise = resolver(values).finally(() => { + promise = null + }) + }, 50) + } +} diff --git a/packages/worker-legacy/package.json b/packages/worker-legacy/package.json new file mode 100644 index 00000000..6d4a3cea --- /dev/null +++ b/packages/worker-legacy/package.json @@ -0,0 +1,9 @@ +{ + "name": "vitest-vscode-worker-legacy", + "version": "0.0.0", + "private": true, + "dependencies": { + "vitest": "$vitest", + "vitest-vscode-shared": "workspace:*" + } +} diff --git a/src/worker/collect.ts b/packages/worker-legacy/src/collect.ts similarity index 100% rename from src/worker/collect.ts rename to packages/worker-legacy/src/collect.ts diff --git a/src/worker/coverage.ts b/packages/worker-legacy/src/coverage.ts similarity index 97% rename from src/worker/coverage.ts rename to packages/worker-legacy/src/coverage.ts index 8739e28a..8a6be75b 100644 --- a/src/worker/coverage.ts +++ b/packages/worker-legacy/src/coverage.ts @@ -4,7 +4,6 @@ import { randomUUID } from 'node:crypto' import { existsSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'pathe' -import { finalCoverageFileName } from '../constants' export class ExtensionCoverageManager { private _enabled = false @@ -14,6 +13,7 @@ export class ExtensionCoverageManager { constructor( private worker: ExtensionWorker, + private finalCoverageFileName: string, ) { this._config = worker.vitest.config.coverage const projects = new Set([...worker.vitest.projects, worker.getRootTestProject()]) @@ -27,6 +27,7 @@ export class ExtensionCoverageManager { }, }) }) + Object.defineProperty(worker.vitest, 'coverageProvider', { get: () => { if (this.enabled) @@ -63,7 +64,7 @@ export class ExtensionCoverageManager { this._config.reporter = [ ['json', { ...jsonReporter?.[1], - file: finalCoverageFileName, + file: this.finalCoverageFileName, }], ] this._config.reportOnFailure = true diff --git a/src/worker/init.ts b/packages/worker-legacy/src/index.ts similarity index 82% rename from src/worker/init.ts rename to packages/worker-legacy/src/index.ts index 9a54229f..35d1b909 100644 --- a/src/worker/init.ts +++ b/packages/worker-legacy/src/index.ts @@ -1,13 +1,19 @@ +import type { WorkerRunnerOptions, WorkerWSEventEmitter } from 'vitest-vscode-shared' import type { UserConfig, WorkspaceProject } from 'vitest/node' -import type { WorkerInitMetadata } from './types' import { Console } from 'node:console' import { Writable } from 'node:stream' -import { pathToFileURL } from 'node:url' import { VSCodeReporter } from './reporter' -import { normalizeDriveLetter } from './utils' +import { ExtensionWorker } from './worker' -export async function initVitest(meta: WorkerInitMetadata, options?: UserConfig) { - const reporter = new VSCodeReporter() +export async function initVitest( + vitestModule: typeof import('vitest/node'), + data: WorkerRunnerOptions, + emitter: WorkerWSEventEmitter, +) { + const meta = data.meta + const reporter = new VSCodeReporter({ + setupFilePath: meta.setupFilePath, + }) let stdout: Writable | undefined let stderr: Writable | undefined @@ -37,9 +43,6 @@ export async function initVitest(meta: WorkerInitMetadata, options?: UserConfig) globalThis.console = new Console(stdout, stderr) } - const vitestModule = await import( - pathToFileURL(normalizeDriveLetter(meta.vitestNodePath)).toString() - ) as typeof import('vitest/node') const pnpExecArgv = meta.pnpApi && meta.pnpLoader ? [ '--require', @@ -53,6 +56,14 @@ export async function initVitest(meta: WorkerInitMetadata, options?: UserConfig) allowUnknownOptions: false, }).options : {} + const options = data.debug + ? { + disableConsoleIntercept: true, + fileParallelism: false, + testTimeout: 0, + hookTimeout: 0, + } + : {} const cliOptions: UserConfig = { config: meta.configFile, ...(meta.workspaceFile ? { workspace: meta.workspaceFile } : {}), @@ -137,5 +148,14 @@ export async function initVitest(meta: WorkerInitMetadata, options?: UserConfig) workspaceSource, configs: Array.from(new Set(configs)), meta, + createWorker() { + return new ExtensionWorker( + vitest, + data.debug, + data.astCollect, + emitter, + data.meta.finalCoverageFileName, + ) + }, } } diff --git a/src/worker/reporter.ts b/packages/worker-legacy/src/reporter.ts similarity index 84% rename from src/worker/reporter.ts rename to packages/worker-legacy/src/reporter.ts index 5bd82957..47674e69 100644 --- a/src/worker/reporter.ts +++ b/packages/worker-legacy/src/reporter.ts @@ -1,18 +1,26 @@ import type { BirpcReturn } from 'birpc' import type { RunnerTestFile, TaskResultPack, UserConsoleLog } from 'vitest' +import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from 'vitest-vscode-shared' import type { Vitest as VitestCore, WorkspaceProject } from 'vitest/node' import type { Reporter } from 'vitest/reporters' -import type { ExtensionWorkerEvents, ExtensionWorkerTransport } from '../api/rpc' import { Console } from 'node:console' import { nextTick } from 'node:process' import { Writable } from 'node:stream' import { parseErrorStacktrace } from '@vitest/utils/source-map' -import { setupFilePath } from '../constants' import { ExtensionWorker } from './worker' +interface VSCodeReporterOptions { + setupFilePath: string +} + export class VSCodeReporter implements Reporter { public rpc!: BirpcReturn private vitest!: VitestCore + private setupFilePath: string + + constructor(options: VSCodeReporterOptions) { + this.setupFilePath = options.setupFilePath + } private get collecting(): boolean { return (this.vitest as any).configOverride.testNamePattern?.toString() === `/${ExtensionWorker.COLLECT_NAME_PATTERN}/` @@ -21,16 +29,16 @@ export class VSCodeReporter implements Reporter { onInit(vitest: VitestCore) { this.vitest = vitest const server = vitest.server.config.server - if (!server.fs.allow.includes(setupFilePath)) - server.fs.allow.push(setupFilePath) + if (!server.fs.allow.includes(this.setupFilePath)) + server.fs.allow.push(this.setupFilePath) vitest.projects.forEach((project) => { project.config.setupFiles = [ ...project.config.setupFiles || [], - setupFilePath, + this.setupFilePath, ] const server = project.server.config.server - if (!server.fs.allow.includes(setupFilePath)) - server.fs.allow.push(setupFilePath) + if (!server.fs.allow.includes(this.setupFilePath)) + server.fs.allow.push(this.setupFilePath) // @ts-expect-error internal, Vitest 3 if (project._initBrowserProvider) { this.overrideInitBrowserProvider(project, '_initBrowserProvider') @@ -44,14 +52,15 @@ export class VSCodeReporter implements Reporter { return } const config = 'vite' in browser ? browser.vite.config.server : browser.config.server - if (!config.fs.allow.includes(setupFilePath)) - config.fs.allow.push(setupFilePath) + if (!config.fs.allow.includes(this.setupFilePath)) + config.fs.allow.push(this.setupFilePath) }) } overrideInitBrowserProvider(project: WorkspaceProject, name: string) { // @ts-expect-error internal const original = project[name].bind(project) + const setupFilePath = this.setupFilePath // @ts-expect-error internal project[name] = async function _initBrowserProvider(this: WorkspaceProject) { await original() @@ -130,20 +139,16 @@ export class VSCodeReporter implements Reporter { this.vitest.logger.outputStream = outputStream } nextTick(() => { - this.rpc.onFinished(files || [], output, collecting) + this.rpc.onTestRunEnd(files || [], output, collecting) }) } onCollected(files?: RunnerTestFile[]) { - this.rpc.onCollected(files, this.collecting) - } - - onWatcherStart(files?: RunnerTestFile[], errors?: unknown[]) { - this.rpc.onWatcherStart(files, errors, this.collecting) + files?.forEach(file => this.rpc.onCollected(file, this.collecting)) } - onWatcherRerun(files: string[], trigger?: string) { - this.rpc.onWatcherRerun(files, trigger, this.collecting) + onWatcherRerun(files: string[]) { + this.rpc.onTestRunStart(files, this.collecting) } toJSON() { diff --git a/packages/worker-legacy/src/types.ts b/packages/worker-legacy/src/types.ts new file mode 100644 index 00000000..cec0bb28 --- /dev/null +++ b/packages/worker-legacy/src/types.ts @@ -0,0 +1,11 @@ +declare module 'vitest' { + export interface ProvidedContext { + __vscode: { + continuousFiles: string[] + watchEveryFile: boolean + rerunTriggered: boolean + } + } +} + +export {} diff --git a/src/worker/watcher.ts b/packages/worker-legacy/src/watcher.ts similarity index 89% rename from src/worker/watcher.ts rename to packages/worker-legacy/src/watcher.ts index bb395036..2cbc8a10 100644 --- a/src/worker/watcher.ts +++ b/packages/worker-legacy/src/watcher.ts @@ -11,11 +11,11 @@ export class ExtensionWorkerWatcher { private enabled = false - constructor(extension: ExtensionWorker) { + constructor(worker: ExtensionWorker) { // eslint-disable-next-line ts/no-this-alias const state = this - const vitest = extension.vitest - ;(extension.getRootTestProject().provide as (key: T, value: ProvidedContext[T]) => void)('__vscode', { + const vitest = worker.vitest + ;(worker.getRootTestProject().provide as (key: T, value: ProvidedContext[T]) => void)('__vscode', { get continuousFiles() { return state.files || [] }, @@ -49,17 +49,17 @@ export class ExtensionWorkerWatcher { const astSpecs: [project: WorkspaceProject, file: string][] = [] for (const [project, file] of specs) { - if (extension.alwaysAstCollect || project.config.browser.enabled) { + if (worker.alwaysAstCollect || project.config.browser.enabled) { astSpecs.push([project, file]) } } - extension.setGlobalTestNamePattern(ExtensionWorker.COLLECT_NAME_PATTERN) + worker.setGlobalTestNamePattern(ExtensionWorker.COLLECT_NAME_PATTERN) vitest.logger.log('Collecting tests due to file changes:', ...files.map(f => relative(vitest.config.root, f))) if (astSpecs.length) { vitest.logger.log('Collecting using AST explorer...') - await extension.astCollect(astSpecs) + await worker.astCollect(astSpecs) this.changedTests.clear() return await originalScheduleRerun.call(this, []) } @@ -70,7 +70,7 @@ export class ExtensionWorkerWatcher { state.rerunTriggered = true const namePattern = state.testNamePattern ? new RegExp(state.testNamePattern) : undefined - extension.setGlobalTestNamePattern(namePattern) + worker.setGlobalTestNamePattern(namePattern) if (state.watchEveryFile) { vitest.logger.log( 'Rerunning all tests due to file changes:', diff --git a/src/worker/worker.ts b/packages/worker-legacy/src/worker.ts similarity index 84% rename from src/worker/worker.ts rename to packages/worker-legacy/src/worker.ts index 5d32e364..cd3453c0 100644 --- a/src/worker/worker.ts +++ b/packages/worker-legacy/src/worker.ts @@ -1,15 +1,22 @@ -import type { ArgumentsType } from 'vitest' -import type { Reporter, ResolvedConfig, TestSpecification, Vitest as VitestCore, WorkspaceProject } from 'vitest/node' -import type { ExtensionWorkerTransport, SerializedTestSpecification } from '../api/rpc' -import type { WorkerWSEventEmitter } from './emitter' +import type { ExtensionTestSpecification, ExtensionWorkerTransport } from 'vitest-vscode-shared' +import type { + Reporter, + ResolvedConfig, + TestSpecification, + Vitest as VitestCore, + WorkspaceProject, +} from 'vitest/node' +import type { WorkerWSEventEmitter } from '../../shared/src/emitter' import { readFileSync } from 'node:fs' import mm from 'micromatch' import { relative } from 'pathe' +import { assert, limitConcurrency } from '../../shared/src/utils' import { astCollectTests, createFailedFileTask } from './collect' import { ExtensionCoverageManager } from './coverage' -import { assert, limitConcurrency } from './utils' import { ExtensionWorkerWatcher } from './watcher' +type ArgumentsType = T extends (...args: infer U) => any ? U : never + export class ExtensionWorker implements ExtensionWorkerTransport { private readonly watcher: ExtensionWorkerWatcher private readonly coverage: ExtensionCoverageManager @@ -21,9 +28,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { private readonly debug = false, public readonly alwaysAstCollect = false, private emitter: WorkerWSEventEmitter, + finalCoverageFileName: string, ) { this.watcher = new ExtensionWorkerWatcher(this) - this.coverage = new ExtensionCoverageManager(this) + this.coverage = new ExtensionCoverageManager(this, finalCoverageFileName) } public get collecting() { @@ -83,8 +91,11 @@ export class ExtensionWorker implements ExtensionWorkerTransport { })(), (async () => { if (otherTests.length) { - const files = otherTests.map( - ([project, filepath]) => [{ name: project.getName() }, filepath], + const files = otherTests.map( + ([project, filepath]) => [ + project.getName(), + filepath, + ] as const, ) try { @@ -114,7 +125,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { this.setTestNamePattern(undefined) } - public async updateSnapshots(files?: SerializedTestSpecification[] | string[] | undefined, testNamePattern?: string | undefined) { + public async updateSnapshots(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePattern?: string | undefined) { this.configOverride.snapshotOptions = { updateSnapshot: 'all', // environment is resolved inside a worker thread @@ -128,17 +139,23 @@ export class ExtensionWorker implements ExtensionWorkerTransport { } } - async resolveTestSpecs(specs: string[] | SerializedTestSpecification[] | undefined): Promise { + async resolveTestSpecs(specs: string[] | ExtensionTestSpecification[] | undefined): Promise { if (!specs || typeof specs[0] === 'string') { const files = await this.globTestSpecifications(specs as string[] | undefined) - return files.map(([project, file]) => { - return [{ name: typeof project === 'string' ? project : project.getName() }, file as string] + return files.map((spec) => { + const project = spec[0] + const file = spec[1] + + return [ + project.getName(), + file, + ] }) } - return (specs || []) as SerializedTestSpecification[] + return (specs as ExtensionTestSpecification[] || []) } - public async runTests(specsOrPaths: SerializedTestSpecification[] | string[] | undefined, testNamePattern?: string) { + public async runTests(specsOrPaths: ExtensionTestSpecification[] | string[] | undefined, testNamePattern?: string) { // @ts-expect-error private method in Vitest <=2.1.5 await this.vitest.initBrowserProviders?.() @@ -188,7 +205,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { }) } - private async runTestFiles(specs: SerializedTestSpecification[], testNamePattern?: string | undefined, runAllFiles = false) { + private async runTestFiles(specs: ExtensionTestSpecification[], testNamePattern?: string | undefined, runAllFiles = false) { await (this.vitest as any).runningPromise this.watcher.markRerun(false) @@ -205,7 +222,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { this.configOverride.testNamePattern = pattern ? new RegExp(pattern) : undefined } - private async rerunTests(specs: SerializedTestSpecification[], runAllFiles = false) { + private async rerunTests(specs: ExtensionTestSpecification[], runAllFiles = false) { const paths = specs.map(spec => spec[1]) const specsToRun = specs.flatMap((spec) => { @@ -217,7 +234,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { if (!fileSpecs.length) { return [] } - return fileSpecs.filter(s => s[0].getName() === spec[0].name) + return fileSpecs.filter(s => s[0].getName() === spec[0]) }) await Promise.all([ this.report('onWatcherRerun', paths), @@ -226,7 +243,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { ...((this.vitest as any)._onUserTestsRerun || []).map((fn: any) => fn(specs)), ]) - await (this.vitest as any).runFiles(specsToRun, runAllFiles) + await this.runFiles(specsToRun, runAllFiles) await this.report('onWatcherStart', this.vitest.state.getFiles(paths)) } @@ -240,6 +257,10 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return ctx.handleFileChanged(file) } + private async runFiles(specs: TestSpecification[], runAllFiles: boolean) { + await (this.vitest as any).runFiles(specs, runAllFiles) + } + private scheduleRerun(files: string[]): Promise { return (this.vitest as any).scheduleRerun(files) } @@ -325,7 +346,7 @@ export class ExtensionWorker implements ExtensionWorkerTransport { return this.watcher.stopTracking() } - watchTests(files?: SerializedTestSpecification[] | string[] | undefined, testNamePatern?: string) { + watchTests(files?: ExtensionTestSpecification[] | string[] | undefined, testNamePatern?: string) { if (files) this.watcher.trackTests(files.map(f => typeof f === 'string' ? f : f[1]), testNamePatern) else @@ -374,4 +395,8 @@ export class ExtensionWorker implements ExtensionWorkerTransport { report(name: T, ...args: ArgumentsType) { return (this.vitest as any).report(name, ...args) } + + initRpc() { + // ignore + } } diff --git a/packages/worker/package.json b/packages/worker/package.json new file mode 100644 index 00000000..58ec159e --- /dev/null +++ b/packages/worker/package.json @@ -0,0 +1,13 @@ +{ + "name": "vitest-vscode-worker", + "type": "module", + "private": true, + "exports": { + ".": "./src/index.ts" + }, + "devDependencies": { + "@vitest/utils": "catalog:", + "vitest": "catalog:", + "vitest-vscode-shared": "workspace:*" + } +} diff --git a/packages/worker/src/coverage.ts b/packages/worker/src/coverage.ts new file mode 100644 index 00000000..060e0c28 --- /dev/null +++ b/packages/worker/src/coverage.ts @@ -0,0 +1,40 @@ +import type { Vitest } from 'vitest/node' +import { existsSync } from 'node:fs' + +const verbose = process.env.VITEST_VSCODE_LOG === 'verbose' + ? (...args: any[]) => { + // eslint-disable-next-line no-console + console.info(...args) + } + : undefined + +export class ExtensionCoverageManager { + private _enabled = false + + constructor(private vitest: Vitest) {} + + async enableCoverage() { + await this.vitest.enableCoverage() + this._enabled = true + } + + disableCoverage() { + this._enabled = false + this.vitest.disableCoverage() + } + + async waitForReport() { + if (!this._enabled) + return null + const vitest = this.vitest + const coverage = vitest.config.coverage + verbose?.(`Waiting for the coverage report to generate: ${coverage.reportsDirectory}`) + await vitest.waitForTestRunEnd() + if (existsSync(coverage.reportsDirectory)) { + verbose?.(`Coverage reports retrieved: ${coverage.reportsDirectory}`) + return coverage.reportsDirectory + } + verbose?.(`Coverage reports directory not found: ${coverage.reportsDirectory}`) + return null + } +} diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts new file mode 100644 index 00000000..e2530fe9 --- /dev/null +++ b/packages/worker/src/index.ts @@ -0,0 +1,172 @@ +import type { WorkerRunnerOptions } from 'vitest-vscode-shared' +import type { WorkerWSEventEmitter } from 'vitest-vscode-shared' +import type { CoverageIstanbulOptions, TestUserConfig } from 'vitest/node' +import { Console } from 'node:console' +import { randomUUID } from 'node:crypto' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { Writable } from 'node:stream' +import { VSCodeReporter } from './reporter' +import { ExtensionWorker } from './worker' + +export async function initVitest( + vitestModule: typeof import('vitest/node'), + data: WorkerRunnerOptions, + emitter: WorkerWSEventEmitter, +) { + const meta = data.meta + const reporter = new VSCodeReporter({ + setupFilePath: meta.setupFilePath, + }) + + let stdout: Writable | undefined + let stderr: Writable | undefined + + if (meta.shellType === 'terminal' && !meta.hasShellIntegration) { + stdout = new Writable({ + write(chunk, __, callback) { + const log = chunk.toString() + if (reporter.rpc) { + reporter.rpc.onProcessLog('stdout', log).catch(() => {}) + } + process.stdout.write(log) + callback() + }, + }) + + stderr = new Writable({ + write(chunk, __, callback) { + const log = chunk.toString() + if (reporter.rpc) { + reporter.rpc.onProcessLog('stderr', log).catch(() => {}) + } + process.stderr.write(log) + callback() + }, + }) + globalThis.console = new Console(stdout, stderr) + } + + const pnpExecArgv = meta.pnpApi && meta.pnpLoader + ? [ + '--require', + meta.pnpApi, + '--experimental-loader', + meta.pnpLoader, + ] + : undefined + const args = meta.arguments + ? vitestModule.parseCLI(meta.arguments, { + allowUnknownOptions: false, + }).options + : {} + const options = data.debug + ? { + disableConsoleIntercept: true, + fileParallelism: false, + testTimeout: 0, + hookTimeout: 0, + } + : {} + const cliOptions: TestUserConfig = { + config: meta.configFile, + ...(meta.workspaceFile ? { workspace: meta.workspaceFile } : {}), + ...args, + ...options, + watch: true, + api: false, + // @ts-expect-error private property + reporter: undefined, + reporters: [reporter], + ui: false, + includeTaskLocation: true, + poolOptions: meta.pnpApi && meta.pnpLoader + ? { + threads: { + execArgv: pnpExecArgv, + }, + forks: { + execArgv: pnpExecArgv, + }, + vmForks: { + execArgv: pnpExecArgv, + }, + vmThreads: { + execArgv: pnpExecArgv, + }, + } + : {}, + } + const vitest = await vitestModule.createVitest( + 'test', + cliOptions, + { + server: { + middlewareMode: true, + watch: null, + }, + plugins: [ + { + name: 'vitest:vscode-extension', + config(userConfig) { + const testConfig = (userConfig as any).test ?? {} + const coverageOptions = (testConfig.coverage ?? {}) as CoverageIstanbulOptions + const reporters = Array.isArray(coverageOptions.reporter) + ? coverageOptions.reporter + : [coverageOptions.reporter] + const jsonReporter = reporters.find(r => r && r[0] === 'json') + const jsonReporterOptions = typeof jsonReporter?.[1] === 'object' ? jsonReporter[1] : {} + coverageOptions.reporter = [ + ['json', { ...jsonReporterOptions, file: meta.finalCoverageFileName }], + ] + return { + test: { + coverage: { + reportOnFailure: true, + reportsDirectory: join(tmpdir(), `vitest-coverage-${randomUUID()}`), + }, + }, + // TODO: type is not augmented + } as any + }, + configResolved(config) { + // stub a server so Vite doesn't start a websocket connection, + // because we don't need it in the extension and it messes up Vite dev command + config.server.hmr = { + server: { + on: () => {}, + off: () => {}, + } as any, + } + }, + }, + ], + }, + { + stderr, + stdout, + }, + ) + await (vitest as any).report('onInit', vitest) + const configs = [ + vitest.getRootProject(), + ...vitest.projects, + ].map(p => p.vite.config.configFile).filter(c => c != null) + const workspaceSource: string | false = (vitest.config.projects != null) + ? vitest.vite.config.configFile || false + : false + return { + vitest, + reporter, + workspaceSource, + configs: Array.from(new Set(configs)), + meta, + createWorker() { + return new ExtensionWorker( + vitest, + data.debug, + emitter, + ) + }, + } +} diff --git a/packages/worker/src/reporter.ts b/packages/worker/src/reporter.ts new file mode 100644 index 00000000..b8969333 --- /dev/null +++ b/packages/worker/src/reporter.ts @@ -0,0 +1,103 @@ +import type { RunnerTaskResultPack, UserConsoleLog } from 'vitest' +import type { VitestWorkerRPC } from 'vitest-vscode-shared' +import type { + Reporter, + RunnerTestFile, + TestModule, + TestProject, + TestSpecification, + Vite, + Vitest as VitestCore, +} from 'vitest/node' + +interface VSCodeReporterOptions { + setupFilePath: string +} + +export class VSCodeReporter implements Reporter { + public rpc!: VitestWorkerRPC + private vitest!: VitestCore + + private setupFilePath: string + + constructor(options: VSCodeReporterOptions) { + this.setupFilePath = options.setupFilePath + } + + onInit(vitest: VitestCore) { + this.vitest = vitest + vitest.projects.forEach((project) => { + this.ensureSetupFileIsAllowed(project.vite.config) + }) + } + + initRpc(rpc: VitestWorkerRPC) { + this.rpc = rpc + } + + onBrowserInit(project: TestProject) { + const config = project.browser!.vite.config + this.ensureSetupFileIsAllowed(config) + } + + onUserConsoleLog(log: UserConsoleLog) { + this.rpc.onConsoleLog(log) + } + + onTaskUpdate(packs: RunnerTaskResultPack[]) { + this.rpc.onTaskUpdate( + // remove the meta because it is not used, + // mark todo tests with a result, because + // it is not set if the test was skipped during collection + packs.map((pack) => { + const task = this.vitest.state.idMap.get(pack[0]) + if (pack[1] || !task) { + return [pack[0], pack[1], {}] + } + + if (task.mode === 'todo' || task.mode === 'skip') { + return [pack[0], { state: task.mode }, {}] + } + + return [pack[0], pack[1], {}] + }), + ) + } + + onTestRunStart(specifications: ReadonlyArray) { + const files = specifications.map(spec => spec.moduleId) + this.rpc.onTestRunStart(Array.from(new Set(files)), false) + } + + onTestRunEnd(testModules: ReadonlyArray, unhandledErrors: ReadonlyArray) { + const files = testModules.map(m => getEntityJSONTask(m)) + + if (unhandledErrors.length) { + // TODO: remove "as unknown[]" + this.vitest.logger.printUnhandledErrors(unhandledErrors as unknown[]) + } + + // as any because Vitest types are different between v3 and v4, + // and shared packages uses the lowest Vitest version + this.rpc.onTestRunEnd(files as any, '', false) + } + + onTestModuleCollected(testModule: TestModule) { + // TODO: is it possible to make types happy with both V3 and V4? + this.rpc.onCollected(getEntityJSONTask(testModule) as any, false) + } + + ensureSetupFileIsAllowed(config: Vite.ResolvedConfig) { + if (!config.server.fs.allow.includes(this.setupFilePath)) { + config.server.fs.allow.push(this.setupFilePath) + } + } + + toJSON() { + return {} + } +} + +function getEntityJSONTask(entity: TestModule) { + return (entity as any).task as RunnerTestFile +} diff --git a/packages/worker/src/runner.ts b/packages/worker/src/runner.ts new file mode 100644 index 00000000..91a10f09 --- /dev/null +++ b/packages/worker/src/runner.ts @@ -0,0 +1,109 @@ +import type { ExtensionTestSpecification, VitestWorkerRPC, WorkerWSEventEmitter } from 'vitest-vscode-shared' +import type { TestSpecification, Vitest as VitestCore } from 'vitest/node' + +export class ExtensionWorkerRunner { + private rpc!: VitestWorkerRPC + + constructor( + public readonly vitest: VitestCore, + private readonly debug = false, + private emitter: WorkerWSEventEmitter, + ) {} + + async getFiles(): Promise { + this.vitest.clearSpecificationsCache() + const specifications = await this.vitest.globTestSpecifications() + return specifications.map(spec => [spec.project.name, spec.moduleId]) + } + + async collectTests(testFiles: ExtensionTestSpecification[]): Promise { + const specifications = await this.resolveTestSpecifications(testFiles) + await this.collectSpecifications(specifications) + } + + public async collectSpecifications(specifications: TestSpecification[]): Promise { + const testModules = await this.vitest.experimental_parseSpecifications(specifications) + const promises = testModules.map(module => + // TODO: fix "as any" + this.rpc.onCollected((module as any).task, true), + ) + await Promise.all(promises) + } + + initRpc(rpc: VitestWorkerRPC) { + this.rpc = rpc + } + + cancelRun(): Promise { + return this.vitest.cancelCurrentRun('keyboard-input') + } + + async runTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + const currentTestNamePattern = this.getGlobalTestNamePattern() + if (testNamePattern) { + this.vitest.setGlobalTestNamePattern(testNamePattern) + } + + if (!filesOrDirectories || this.isOnlyDirectories(filesOrDirectories)) { + const specifications = await this.vitest.getRelevantTestSpecifications(filesOrDirectories) + await this.vitest.rerunTestSpecifications(specifications, true) + } + else { + const specifications = await this.resolveTestSpecifications(filesOrDirectories) + await this.vitest.rerunTestSpecifications(specifications, false) + } + + // debugger never runs in watch mode + if (this.debug) { + await this.vitest.close() + this.emitter.close() + } + + if (currentTestNamePattern) { + this.vitest.setGlobalTestNamePattern(currentTestNamePattern) + } + else { + this.vitest.resetGlobalTestNamePattern() + } + } + + // TODO: use vitest.getGlobalTestNamePattern when merged + private getGlobalTestNamePattern(): RegExp | undefined { + if ((this.vitest as any).configOverride.testNamePattern != null) { + return (this.vitest as any).configOverride.testNamePattern + } + return this.vitest.config.testNamePattern + } + + async resolveTestSpecifications(files: ExtensionTestSpecification[]): Promise { + const specifications: TestSpecification[] = [] + files.forEach((file) => { + const [projectName, filepath] = file + const project = this.vitest.getProjectByName(projectName) + specifications.push(project.createSpecification(filepath)) + }) + return specifications + } + + async updateSnapshots(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + const currentTestNamePattern = this.getGlobalTestNamePattern() + this.vitest.enableSnapshotUpdate() + try { + return await this.runTests(filesOrDirectories, testNamePattern) + } + finally { + if (currentTestNamePattern) { + this.vitest.setGlobalTestNamePattern(currentTestNamePattern) + } + else { + this.vitest.resetSnapshotUpdate() + } + } + } + + private isOnlyDirectories( + filesOrDirectories: ExtensionTestSpecification[] | string[], + ): filesOrDirectories is string[] { + return typeof filesOrDirectories[0] === 'string' + } +} diff --git a/packages/worker/src/watcher.ts b/packages/worker/src/watcher.ts new file mode 100644 index 00000000..8183423a --- /dev/null +++ b/packages/worker/src/watcher.ts @@ -0,0 +1,87 @@ +import type { TestSpecification, Vitest } from 'vitest/node' +import type { ExtensionWorkerRunner } from './runner' +import { createQueuedHandler, type ExtensionTestSpecification } from 'vitest-vscode-shared' + +export class ExtensionWorkerWatcher { + private enabled = false + private trackingEveryFile = false + private trackedTestItems: Record = {} + private trackedDirectories: string[] = [] + + constructor(vitest: Vitest, private runner: ExtensionWorkerRunner) { + vitest.onFilterWatchedSpecification((specification) => { + const shouldRun = this.shouldRunSpecification(specification) + if (shouldRun) { + return true + } + this.scheduleAstCollection(specification) + return false + }) + } + + private scheduleAstCollection = createQueuedHandler(async (specifications) => { + await this.runner.collectSpecifications(specifications) + }) + + private shouldRunSpecification(specification: TestSpecification) { + if (!this.enabled) { + return false + } + + if (this.trackingEveryFile) { + return true + } + + if (this.isTestFileWatched(specification.moduleId, this.trackedDirectories)) { + return true + } + + const project = specification.project.name + const files = this.trackedTestItems[project] + if (!files?.length) { + return false + } + + return files.includes(specification.moduleId) + } + + trackTestItems(filesOrDirectories: ExtensionTestSpecification[] | string[]) { + this.enabled = true + if (typeof filesOrDirectories[0] === 'string') { + this.trackedDirectories = filesOrDirectories as string[] + } + else { + for (const [project, file] of filesOrDirectories) { + if (!this.trackedTestItems[project]) { + this.trackedTestItems[project] = [] + } + this.trackedTestItems[project].push(file) + } + } + } + + trackEveryFile(): void { + this.enabled = true + this.trackingEveryFile = true + } + + stopTracking() { + this.enabled = false + this.trackingEveryFile = false + this.trackedTestItems = {} + this.trackedDirectories = [] + } + + private isTestFileWatched(testFile: string, files: string[]) { + if (!files?.length) + return false + + return files.some((file) => { + if (file === testFile) + return true + if (file[file.length - 1] === '/') + return testFile.startsWith(file) + return false + }) + } +} diff --git a/packages/worker/src/worker.ts b/packages/worker/src/worker.ts new file mode 100644 index 00000000..52548d2e --- /dev/null +++ b/packages/worker/src/worker.ts @@ -0,0 +1,101 @@ +import type { + ExtensionTestSpecification, + ExtensionWorkerTransport, + VitestWorkerRPC, + WorkerWSEventEmitter, +} from 'vitest-vscode-shared' +import type { Vitest as VitestCore } from 'vitest/node' +import { ExtensionCoverageManager } from './coverage' +import { ExtensionWorkerRunner } from './runner' +import { ExtensionWorkerWatcher } from './watcher' + +export class ExtensionWorker implements ExtensionWorkerTransport { + private readonly watcher: ExtensionWorkerWatcher + private readonly coverage: ExtensionCoverageManager + private readonly runner: ExtensionWorkerRunner + + constructor( + public readonly vitest: VitestCore, + debug = false, + emitter: WorkerWSEventEmitter, + ) { + this.runner = new ExtensionWorkerRunner(vitest, debug, emitter) + this.watcher = new ExtensionWorkerWatcher(vitest, this.runner) + this.coverage = new ExtensionCoverageManager(vitest) + } + + async getFiles(): Promise { + return this.runner.getFiles() + } + + async collectTests(testFiles: ExtensionTestSpecification[]): Promise { + return this.runner.collectTests(testFiles) + } + + cancelRun(): Promise { + return this.runner.cancelRun() + } + + async runTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + await this.runner.runTests(filesOrDirectories, testNamePattern) + } + + async updateSnapshots(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): Promise { + return this.runner.updateSnapshots(filesOrDirectories, testNamePattern) + } + + watchTests(filesOrDirectories?: ExtensionTestSpecification[] | string[], testNamePattern?: string): void { + if (testNamePattern) { + this.vitest.setGlobalTestNamePattern(testNamePattern) + } + + if (!filesOrDirectories) { + this.watcher.trackEveryFile() + } + else { + this.watcher.trackTestItems(filesOrDirectories) + } + } + + unwatchTests() { + this.watcher.stopTracking() + } + + async invalidateIstanbulTestModules(): Promise { + // do nothing, because Vitest 4 supports this out of the box + } + + async enableCoverage(): Promise { + await this.coverage.enableCoverage() + } + + disableCoverage(): void { + this.coverage.disableCoverage() + } + + waitForCoverageReport(): Promise { + return this.coverage.waitForReport() + } + + onFilesChanged(files: string[]): void { + files.forEach(file => this.vitest.watcher.onFileChange(file)) + } + + onFilesCreated(files: string[]): void { + files.forEach(file => this.vitest.watcher.onFileCreate(file)) + } + + dispose() { + this.coverage.disableCoverage() + this.watcher.stopTracking() + return this.vitest.close() + } + + close() { + return this.dispose() + } + + initRpc(rpc: VitestWorkerRPC) { + this.runner.initRpc(rpc) + } +} diff --git a/packages/worker/tsconfig.json b/packages/worker/tsconfig.json new file mode 100644 index 00000000..c9bb7b12 --- /dev/null +++ b/packages/worker/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": ".", + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "noEmit": true, + "outDir": "dist" + }, + "include": [ + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules", + "dist" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 02d34a3d..6cac75f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,24 +6,33 @@ settings: catalogs: default: - '@vitest/browser': - specifier: ^3.2.2 - version: 3.2.3 '@vitest/coverage-v8': - specifier: ^3.2.2 - version: 3.2.3 - '@vitest/runner': - specifier: ^3.2.2 - version: 3.2.3 + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8 '@vitest/utils': - specifier: ^3.2.2 - version: 3.2.3 + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8 vite: - specifier: ^6.3.5 - version: 6.3.5 - vitest: - specifier: ^3.2.2 - version: 3.2.3 + specifier: ^7.1.0 + version: 7.1.2 + +overrides: + '@vitest/browser': ^4.0.0-beta.8 + '@vitest/coverage': ^4.0.0-beta.8 + '@vitest/runner': ^4.0.0-beta.8 + vitest: ^4.0.0-beta.8 + vitest-vscode-extension>@vitest/browser: ^3.2.0 + vitest-vscode-extension>@vitest/coverage: ^3.2.0 + vitest-vscode-extension>@vitest/runner: ^3.2.0 + vitest-vscode-extension>vitest: ^3.2.0 + vitest-vscode-shared>@vitest/browser: ^3.2.0 + vitest-vscode-shared>@vitest/coverage: ^3.2.0 + vitest-vscode-shared>@vitest/runner: ^3.2.0 + vitest-vscode-shared>vitest: ^3.2.0 + vitest-vscode-worker-legacy>@vitest/browser: ^3.2.0 + vitest-vscode-worker-legacy>@vitest/coverage: ^3.2.0 + vitest-vscode-worker-legacy>@vitest/runner: ^3.2.0 + vitest-vscode-worker-legacy>vitest: ^3.2.0 importers: @@ -31,7 +40,7 @@ importers: devDependencies: '@antfu/eslint-config': specifier: ^4.14.1 - version: 4.14.1(@vue/compiler-sfc@3.5.16)(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.2.3) + version: 4.14.1(@vue/compiler-sfc@3.5.16)(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@4.0.0-beta.8) '@playwright/test': specifier: ^1.42.1 version: 1.52.0 @@ -63,11 +72,11 @@ importers: specifier: ^8.5.10 version: 8.18.1 '@vitest/runner': - specifier: 'catalog:' - version: 3.2.3 + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8 '@vitest/utils': specifier: 'catalog:' - version: 3.2.3 + version: 4.0.0-beta.8 '@vscode/test-cli': specifier: ^0.0.6 version: 0.0.6 @@ -136,7 +145,7 @@ importers: version: 7.7.2 tsup: specifier: ^8.0.1 - version: 8.5.0(@swc/core@1.3.107(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.4)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) + version: 8.5.0(@swc/core@1.3.107(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.7.1 version: 4.19.4 @@ -144,8 +153,8 @@ importers: specifier: ^5.6.2 version: 5.8.3 vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) which: specifier: ^4.0.0 version: 4.0.0 @@ -153,6 +162,45 @@ importers: specifier: ^8.16.0 version: 8.18.2 + packages/extension: + dependencies: + vitest: + specifier: ^3.2.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest-vscode-shared: + specifier: workspace:* + version: link:../shared + + packages/shared: + devDependencies: + birpc: + specifier: 2.4.0 + version: 2.4.0 + vitest: + specifier: ^3.2.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + + packages/worker: + devDependencies: + '@vitest/utils': + specifier: 'catalog:' + version: 4.0.0-beta.8 + vitest: + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest-vscode-shared: + specifier: workspace:* + version: link:../shared + + packages/worker-legacy: + dependencies: + vitest: + specifier: ^3.2.0 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest-vscode-shared: + specifier: workspace:* + version: link:../shared + samples/ast-collector: dependencies: birpc: @@ -161,13 +209,13 @@ importers: devDependencies: '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3) + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/basic: dependencies: @@ -177,13 +225,28 @@ importers: devDependencies: '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3) + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vitest: + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + + samples/basic-v4: + devDependencies: + '@vitest/browser': + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) + '@vitest/coverage-v8': + specifier: 'catalog:' + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) + vite: specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vitest: + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/browser: dependencies: @@ -192,32 +255,32 @@ importers: version: 0.2.19 devDependencies: '@vitest/browser': - specifier: 'catalog:' - version: 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3) + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) playwright: specifier: ^1.47.0 version: 1.52.0 vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/continuous: devDependencies: vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/e2e: devDependencies: vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/imba: devDependencies: @@ -229,43 +292,43 @@ importers: version: 6.6.3 imba: specifier: ^2.0.0-alpha.235 - version: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.2)(vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + version: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) jsdom: specifier: ^24.0.0 version: 24.1.3 vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vite-plugin-imba: specifier: ^0.10.3 - version: 0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.2)(vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + version: 0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) vitest-github-actions-reporter-temp: specifier: ^0.8.3 - version: 0.8.3(vitest@3.2.3) + version: 0.8.3(vitest@4.0.0-beta.8) samples/in-source: devDependencies: vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/monorepo-vitest-workspace: devDependencies: '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3) + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) happy-dom: specifier: ^15.7.4 version: 15.11.7 vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/monorepo-vitest-workspace/packages/react: dependencies: @@ -320,20 +383,20 @@ importers: samples/multiple-configs: dependencies: vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/no-config: devDependencies: vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/readme: devDependencies: vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) samples/vue: dependencies: @@ -343,10 +406,10 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: ^5.0.4 - version: 5.2.4(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) + version: 5.2.4(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3)) '@vitest/coverage-v8': specifier: 'catalog:' - version: 3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3) + version: 4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8) '@vue/test-utils': specifier: ^2.4.5 version: 2.4.6 @@ -355,10 +418,10 @@ importers: version: 14.7.1 vite: specifier: 'catalog:' - version: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + version: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vitest: - specifier: 'catalog:' - version: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@14.7.1)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + specifier: ^4.0.0-beta.8 + version: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@14.7.1)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) packages: @@ -1009,101 +1072,201 @@ packages: cpu: [arm] os: [android] + '@rollup/rollup-android-arm-eabi@4.46.2': + resolution: {integrity: sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==} + cpu: [arm] + os: [android] + '@rollup/rollup-android-arm64@4.42.0': resolution: {integrity: sha512-bpRipfTgmGFdCZDFLRvIkSNO1/3RGS74aWkJJTFJBH7h3MRV4UijkaEUeOMbi9wxtxYmtAbVcnMtHTPBhLEkaw==} cpu: [arm64] os: [android] + '@rollup/rollup-android-arm64@4.46.2': + resolution: {integrity: sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==} + cpu: [arm64] + os: [android] + '@rollup/rollup-darwin-arm64@4.42.0': resolution: {integrity: sha512-JxHtA081izPBVCHLKnl6GEA0w3920mlJPLh89NojpU2GsBSB6ypu4erFg/Wx1qbpUbepn0jY4dVWMGZM8gplgA==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-arm64@4.46.2': + resolution: {integrity: sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.42.0': resolution: {integrity: sha512-rv5UZaWVIJTDMyQ3dCEK+m0SAn6G7H3PRc2AZmExvbDvtaDc+qXkei0knQWcI3+c9tEs7iL/4I4pTQoPbNL2SA==} cpu: [x64] os: [darwin] + '@rollup/rollup-darwin-x64@4.46.2': + resolution: {integrity: sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-freebsd-arm64@4.42.0': resolution: {integrity: sha512-fJcN4uSGPWdpVmvLuMtALUFwCHgb2XiQjuECkHT3lWLZhSQ3MBQ9pq+WoWeJq2PrNxr9rPM1Qx+IjyGj8/c6zQ==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.46.2': + resolution: {integrity: sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.42.0': resolution: {integrity: sha512-CziHfyzpp8hJpCVE/ZdTizw58gr+m7Y2Xq5VOuCSrZR++th2xWAz4Nqk52MoIIrV3JHtVBhbBsJcAxs6NammOQ==} cpu: [x64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.46.2': + resolution: {integrity: sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.42.0': resolution: {integrity: sha512-UsQD5fyLWm2Fe5CDM7VPYAo+UC7+2Px4Y+N3AcPh/LdZu23YcuGPegQly++XEVaC8XUTFVPscl5y5Cl1twEI4A==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + resolution: {integrity: sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.42.0': resolution: {integrity: sha512-/i8NIrlgc/+4n1lnoWl1zgH7Uo0XK5xK3EDqVTf38KvyYgCU/Rm04+o1VvvzJZnVS5/cWSd07owkzcVasgfIkQ==} cpu: [arm] os: [linux] + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + resolution: {integrity: sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.42.0': resolution: {integrity: sha512-eoujJFOvoIBjZEi9hJnXAbWg+Vo1Ov8n/0IKZZcPZ7JhBzxh2A+2NFyeMZIRkY9iwBvSjloKgcvnjTbGKHE44Q==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-gnu@4.46.2': + resolution: {integrity: sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-arm64-musl@4.42.0': resolution: {integrity: sha512-/3NrcOWFSR7RQUQIuZQChLND36aTU9IYE4j+TB40VU78S+RA0IiqHR30oSh6P1S9f9/wVOenHQnacs/Byb824g==} cpu: [arm64] os: [linux] + '@rollup/rollup-linux-arm64-musl@4.46.2': + resolution: {integrity: sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==} + cpu: [arm64] + os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.42.0': resolution: {integrity: sha512-O8AplvIeavK5ABmZlKBq9/STdZlnQo7Sle0LLhVA7QT+CiGpNVe197/t8Aph9bhJqbDVGCHpY2i7QyfEDDStDg==} cpu: [loong64] os: [linux] + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + resolution: {integrity: sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==} + cpu: [loong64] + os: [linux] + '@rollup/rollup-linux-powerpc64le-gnu@4.42.0': resolution: {integrity: sha512-6Qb66tbKVN7VyQrekhEzbHRxXXFFD8QKiFAwX5v9Xt6FiJ3BnCVBuyBxa2fkFGqxOCSGGYNejxd8ht+q5SnmtA==} cpu: [ppc64] os: [linux] + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + resolution: {integrity: sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==} + cpu: [ppc64] + os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.42.0': resolution: {integrity: sha512-KQETDSEBamQFvg/d8jajtRwLNBlGc3aKpaGiP/LvEbnmVUKlFta1vqJqTrvPtsYsfbE/DLg5CC9zyXRX3fnBiA==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + resolution: {integrity: sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.42.0': resolution: {integrity: sha512-qMvnyjcU37sCo/tuC+JqeDKSuukGAd+pVlRl/oyDbkvPJ3awk6G6ua7tyum02O3lI+fio+eM5wsVd66X0jQtxw==} cpu: [riscv64] os: [linux] + '@rollup/rollup-linux-riscv64-musl@4.46.2': + resolution: {integrity: sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==} + cpu: [riscv64] + os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.42.0': resolution: {integrity: sha512-I2Y1ZUgTgU2RLddUHXTIgyrdOwljjkmcZ/VilvaEumtS3Fkuhbw4p4hgHc39Ypwvo2o7sBFNl2MquNvGCa55Iw==} cpu: [s390x] os: [linux] + '@rollup/rollup-linux-s390x-gnu@4.46.2': + resolution: {integrity: sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==} + cpu: [s390x] + os: [linux] + '@rollup/rollup-linux-x64-gnu@4.42.0': resolution: {integrity: sha512-Gfm6cV6mj3hCUY8TqWa63DB8Mx3NADoFwiJrMpoZ1uESbK8FQV3LXkhfry+8bOniq9pqY1OdsjFWNsSbfjPugw==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-gnu@4.46.2': + resolution: {integrity: sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-linux-x64-musl@4.42.0': resolution: {integrity: sha512-g86PF8YZ9GRqkdi0VoGlcDUb4rYtQKyTD1IVtxxN4Hpe7YqLBShA7oHMKU6oKTCi3uxwW4VkIGnOaH/El8de3w==} cpu: [x64] os: [linux] + '@rollup/rollup-linux-x64-musl@4.46.2': + resolution: {integrity: sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==} + cpu: [x64] + os: [linux] + '@rollup/rollup-win32-arm64-msvc@4.42.0': resolution: {integrity: sha512-+axkdyDGSp6hjyzQ5m1pgcvQScfHnMCcsXkx8pTgy/6qBmWVhtRVlgxjWwDp67wEXXUr0x+vD6tp5W4x6V7u1A==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.46.2': + resolution: {integrity: sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.42.0': resolution: {integrity: sha512-F+5J9pelstXKwRSDq92J0TEBXn2nfUrQGg+HK1+Tk7VOL09e0gBqUHugZv7SW4MGrYj41oNCUe3IKCDGVlis2g==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.46.2': + resolution: {integrity: sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.42.0': resolution: {integrity: sha512-LpHiJRwkaVz/LqjHjK8LCi8osq7elmpwujwbXKNW88bM8eeGxavJIKKjkjpMHAh/2xfnrt1ZSnhTv41WYUHYmA==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.46.2': + resolution: {integrity: sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==} + cpu: [x64] + os: [win32] + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -1244,8 +1407,8 @@ packages: '@swc/types@0.1.22': resolution: {integrity: sha512-D13mY/ZA4PPEFSy6acki9eBT/3WgjMoRqNcdpIvjaYLQ44Xk5BdaL7UkDxAh6Z9UOe7tCCp67BVmZCojYp9owg==} - '@testing-library/dom@10.4.0': - resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} '@testing-library/dom@9.3.4': @@ -1456,12 +1619,12 @@ packages: vite: ^5.0.0 || ^6.0.0 vue: ^3.2.25 - '@vitest/browser@3.2.3': - resolution: {integrity: sha512-5HpUb0ixGF8JWSAjb/P1x/VPuTYUkL4pL0+YO6DJiuvQgqJN3PREaUEcXwfXjU4nBc37EahfpRbAwdE9pHs9lQ==} + '@vitest/browser@4.0.0-beta.8': + resolution: {integrity: sha512-xFMdlTkOT5ml2YYkWIxryH/UQjerLdBA81GeiIoOdxxbupFLzQ+z27XU4uqzzmliHx/BTzG6Hb9XfWbvsjmlZA==} peerDependencies: playwright: '*' safaridriver: '*' - vitest: 3.2.3 + vitest: ^4.0.0-beta.8 webdriverio: ^7.0.0 || ^8.0.0 || ^9.0.0 peerDependenciesMeta: playwright: @@ -1471,11 +1634,11 @@ packages: webdriverio: optional: true - '@vitest/coverage-v8@3.2.3': - resolution: {integrity: sha512-D1QKzngg8PcDoCE8FHSZhREDuEy+zcKmMiMafYse41RZpBE5EDJyKOTdqK3RQfsV2S2nyKor5KCs8PyPRFqKPg==} + '@vitest/coverage-v8@4.0.0-beta.8': + resolution: {integrity: sha512-vd8D6P/PWtHXjH5Sot8PuGKchYC7Uns46Dpr1fg57kD8SOdXJybxtg/EdYvD5/OUW2q0iQCFes79WbH+LelwaA==} peerDependencies: - '@vitest/browser': 3.2.3 - vitest: 3.2.3 + '@vitest/browser': ^4.0.0-beta.8 + vitest: ^4.0.0-beta.8 peerDependenciesMeta: '@vitest/browser': optional: true @@ -1485,18 +1648,21 @@ packages: peerDependencies: eslint: '>= 8.57.0' typescript: '>= 5.0.0' - vitest: '*' + vitest: ^4.0.0-beta.8 peerDependenciesMeta: typescript: optional: true vitest: optional: true - '@vitest/expect@3.2.3': - resolution: {integrity: sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/expect@4.0.0-beta.8': + resolution: {integrity: sha512-40DZ7nl5AkASkDNaVR7TqsbeJCs5D+dyQNM5cIvgjG3KK+ATeWxtXJbmRNqgdbq+FL3v/pchnrJM1R9BFkTdUQ==} - '@vitest/mocker@3.2.3': - resolution: {integrity: sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA==} + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 @@ -1506,20 +1672,43 @@ packages: vite: optional: true - '@vitest/pretty-format@3.2.3': - resolution: {integrity: sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng==} + '@vitest/mocker@4.0.0-beta.8': + resolution: {integrity: sha512-a5ynR/Fsrciuq17i8lzS5NH3ICxwFZMlQ4bXPzGV+KlIcVHu20a/8q6KekqUaVBxCbmohORLFXFx9IptHS9gXA==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/pretty-format@4.0.0-beta.8': + resolution: {integrity: sha512-sr5HPeeRff4gTpDwI2Kvz8dS2CmDCCZ1PRu3IOeLTcSJjhEWmk3IJILjqaA8yyj+QzWjnqAxr2rmZNpO0h/5Vw==} + + '@vitest/runner@4.0.0-beta.8': + resolution: {integrity: sha512-m7jrT+KMEgONclBI7y3ptUD+/uhRzzHLblws84fo+WesCjS8WT8q7RoPMgqymj5kmzIF5sTh6NOvrNEE5LaLCQ==} - '@vitest/runner@3.2.3': - resolution: {integrity: sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w==} + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} - '@vitest/snapshot@3.2.3': - resolution: {integrity: sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA==} + '@vitest/snapshot@4.0.0-beta.8': + resolution: {integrity: sha512-2hzc/ksGlZ8Rcg11VD/AhTwSaPEsdtrNA+TCV4w6tuZ7I2X+XJXimfk/Cehz5zMgfFuV8tFmGimb/BpyIbNiMg==} - '@vitest/spy@3.2.3': - resolution: {integrity: sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/utils@3.2.3': - resolution: {integrity: sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q==} + '@vitest/spy@4.0.0-beta.8': + resolution: {integrity: sha512-WfHF35GCf5xx3B1oRrhWMVAfcBBYO4WHAYLbeeaZ1ZSW5/VBXJ/M37bLxRRKnXcgffwcsWA7xpjWnL0dQ1q5NA==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@vitest/utils@4.0.0-beta.8': + resolution: {integrity: sha512-+eV3yrDooGMnHHVQ1aqzPJIBlxEsaQg/5BVFRSkbBgfiKqM9HZbqYK736s0D8tfdMLOjeB+7u7Tw0emd/h3MlQ==} '@vscode/test-cli@0.0.6': resolution: {integrity: sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==} @@ -1872,6 +2061,10 @@ packages: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} + chai@5.2.1: + resolution: {integrity: sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==} + engines: {node: '>=18'} + chalk@3.0.0: resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} engines: {node: '>=8'} @@ -2603,8 +2796,8 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} - expect-type@1.2.1: - resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} engines: {node: '>=12.0.0'} exsolve@1.0.5: @@ -2959,7 +3152,7 @@ packages: '@testing-library/jest-dom': '*' vite: '*' vite-node: '*' - vitest: '*' + vitest: ^4.0.0-beta.8 peerDependenciesMeta: '@testing-library/dom': optional: true @@ -3419,6 +3612,9 @@ packages: loupe@3.1.3: resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + loupe@3.2.0: + resolution: {integrity: sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -3992,10 +4188,18 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pirates@4.0.7: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pixelmatch@7.1.0: + resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} + hasBin: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -4019,6 +4223,10 @@ packages: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + pnpm-workspace-yaml@0.3.1: resolution: {integrity: sha512-3nW5RLmREmZ8Pm8MbPsO2RM+99RRjYd25ynj3NV0cFsN7CcEl4sDFzgoFmSyduFwxFQ2Qbu3y2UdCh6HlyUOeA==} @@ -4052,6 +4260,10 @@ packages: resolution: {integrity: sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + prebuild-install@7.1.3: resolution: {integrity: sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==} engines: {node: '>=10'} @@ -4230,6 +4442,11 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + rollup@4.46.2: + resolution: {integrity: sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + rrweb-cssom@0.7.1: resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==} @@ -4367,6 +4584,7 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions sourcemap-codec@1.4.8: resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} @@ -4537,10 +4755,6 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - test-exclude@7.0.1: - resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==} - engines: {node: '>=18'} - text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -4572,8 +4786,8 @@ packages: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} - tinypool@1.1.0: - resolution: {integrity: sha512-7CotroY9a8DKsKprEy/a14aCCm8jYVmR7aFy4fpkZM8sdpNJbKkixuNjgM50yCmip2ezc8z4N7k3oe2+rfRJCQ==} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} engines: {node: ^18.0.0 || >=20.0.0} tinyrainbow@2.0.0: @@ -4785,8 +4999,8 @@ packages: resolution: {integrity: sha512-gjb0ARm9qlcBAonU4zPwkl9ecKkas+tC2CGwFfptTCWWIVTWY1YUbT2zZKsOAF1jR/tNxxyLwwG0cb42XlYcTg==} engines: {node: '>=4'} - vite-node@3.2.3: - resolution: {integrity: sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ==} + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -4801,19 +5015,19 @@ packages: diff-match-patch: optional: true - vite@6.3.5: - resolution: {integrity: sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite@7.1.2: + resolution: {integrity: sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@types/node': ^20.19.0 || >=22.12.0 jiti: '>=1.21.0' - less: '*' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 yaml: ^2.4.2 @@ -4845,18 +5059,46 @@ packages: resolution: {integrity: sha512-JnBZW20iSnt9cwadfeL1E5UXmsSucsCWNC2mvppjPOXAXdS76WvwQW2VKahoAuyB5iQOREVO3pkzmID+yK6orQ==} engines: {node: '>=14'} peerDependencies: - vitest: '>=0.16.0' + vitest: ^4.0.0-beta.8 - vitest@3.2.3: - resolution: {integrity: sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} 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.2.3 - '@vitest/ui': 3.2.3 + '@vitest/browser': ^4.0.0-beta.8 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vitest@4.0.0-beta.8: + resolution: {integrity: sha512-wN/RDeCd5uXHV6tELw4AJzeP5rxR4YWXN3ems+59ZummmiovNjlfwG+CEZp5GitlxDQu7muoY4VPrSUxPzzKiQ==} + 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': ^4.0.0-beta.8 + '@vitest/ui': 4.0.0-beta.8 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -4993,6 +5235,18 @@ packages: utf-8-validate: optional: true + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -5101,7 +5355,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@antfu/eslint-config@4.14.1(@vue/compiler-sfc@3.5.16)(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.2.3)': + '@antfu/eslint-config@4.14.1(@vue/compiler-sfc@3.5.16)(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@4.0.0-beta.8)': dependencies: '@antfu/install-pkg': 1.1.0 '@clack/prompts': 0.11.0 @@ -5110,7 +5364,7 @@ snapshots: '@stylistic/eslint-plugin': 5.0.0-beta.3(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/eslint-plugin': 8.34.0(@typescript-eslint/parser@8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3))(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) '@typescript-eslint/parser': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) - '@vitest/eslint-plugin': 1.2.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.2.3) + '@vitest/eslint-plugin': 1.2.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@4.0.0-beta.8) ansis: 4.1.0 cac: 6.7.14 eslint: 9.28.0(jiti@2.4.2) @@ -5735,63 +5989,123 @@ snapshots: '@rollup/rollup-android-arm-eabi@4.42.0': optional: true + '@rollup/rollup-android-arm-eabi@4.46.2': + optional: true + '@rollup/rollup-android-arm64@4.42.0': optional: true + '@rollup/rollup-android-arm64@4.46.2': + optional: true + '@rollup/rollup-darwin-arm64@4.42.0': optional: true + '@rollup/rollup-darwin-arm64@4.46.2': + optional: true + '@rollup/rollup-darwin-x64@4.42.0': optional: true + '@rollup/rollup-darwin-x64@4.46.2': + optional: true + '@rollup/rollup-freebsd-arm64@4.42.0': optional: true + '@rollup/rollup-freebsd-arm64@4.46.2': + optional: true + '@rollup/rollup-freebsd-x64@4.42.0': optional: true + '@rollup/rollup-freebsd-x64@4.46.2': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.42.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.46.2': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.42.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.46.2': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.42.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-arm64-musl@4.42.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.46.2': + optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.42.0': optional: true + '@rollup/rollup-linux-loongarch64-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-powerpc64le-gnu@4.42.0': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.42.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.42.0': optional: true + '@rollup/rollup-linux-riscv64-musl@4.46.2': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.42.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-x64-gnu@4.42.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.46.2': + optional: true + '@rollup/rollup-linux-x64-musl@4.42.0': optional: true + '@rollup/rollup-linux-x64-musl@4.46.2': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.42.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.46.2': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.42.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.46.2': + optional: true + '@rollup/rollup-win32-x64-msvc@4.42.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.46.2': + optional: true + '@sec-ant/readable-stream@0.4.1': {} '@secretlint/config-creator@9.3.4': @@ -5945,15 +6259,15 @@ snapshots: '@swc/counter': 0.1.3 optional: true - '@testing-library/dom@10.4.0': + '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.27.1 '@babel/runtime': 7.27.6 '@types/aria-query': 5.0.4 aria-query: 5.3.0 - chalk: 4.1.2 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 + picocolors: 1.1.1 pretty-format: 27.5.1 '@testing-library/dom@9.3.4': @@ -5977,9 +6291,9 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.0)': + '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: - '@testing-library/dom': 10.4.0 + '@testing-library/dom': 10.4.1 '@textlint/ast-node-types@14.8.0': {} @@ -6221,22 +6535,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))': + '@vitejs/plugin-vue@5.2.4(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vue@3.5.16(typescript@5.8.3))': dependencies: - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) vue: 3.5.16(typescript@5.8.3) - '@vitest/browser@3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3)': + '@vitest/browser@4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.4)': dependencies: - '@testing-library/dom': 10.4.0 - '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) - '@vitest/utils': 3.2.3 + '@testing-library/dom': 10.4.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/mocker': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/utils': 4.0.0-beta.8 magic-string: 0.30.17 + pixelmatch: 7.1.0 + pngjs: 7.0.0 sirv: 3.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) - ws: 8.18.2 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + ws: 8.18.3 optionalDependencies: playwright: 1.52.0 transitivePeerDependencies: @@ -6244,79 +6560,134 @@ snapshots: - msw - utf-8-validate - vite + optional: true - '@vitest/coverage-v8@3.2.3(@vitest/browser@3.2.3)(vitest@3.2.3)': + '@vitest/browser@4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) + '@vitest/mocker': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/utils': 4.0.0-beta.8 + magic-string: 0.30.17 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.1 + tinyrainbow: 2.0.0 + vitest: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + ws: 8.18.3 + optionalDependencies: + playwright: 1.52.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/coverage-v8@4.0.0-beta.8(@vitest/browser@4.0.0-beta.8)(vitest@4.0.0-beta.8)': dependencies: - '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.0-beta.8 ast-v8-to-istanbul: 0.3.3 debug: 4.4.1(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.1.7 - magic-string: 0.30.17 magicast: 0.3.5 std-env: 3.9.0 - test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) optionalDependencies: - '@vitest/browser': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + '@vitest/browser': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) transitivePeerDependencies: - supports-color - '@vitest/eslint-plugin@1.2.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@3.2.3)': + '@vitest/eslint-plugin@1.2.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3)(vitest@4.0.0-beta.8)': dependencies: '@typescript-eslint/utils': 8.34.0(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) eslint: 9.28.0(jiti@2.4.2) optionalDependencies: typescript: 5.8.3 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@vitest/expect@3.2.3': + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 + tinyrainbow: 2.0.0 + + '@vitest/expect@4.0.0-beta.8': dependencies: '@types/chai': 5.2.2 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 - chai: 5.2.0 + '@vitest/spy': 4.0.0-beta.8 + '@vitest/utils': 4.0.0-beta.8 + chai: 5.2.1 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))': + '@vitest/mocker@3.2.4(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))': dependencies: - '@vitest/spy': 3.2.3 + '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: msw: 2.10.2(@types/node@24.0.0)(typescript@5.8.3) - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - '@vitest/pretty-format@3.2.3': + '@vitest/mocker@4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))': + dependencies: + '@vitest/spy': 4.0.0-beta.8 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + msw: 2.10.2(@types/node@24.0.0)(typescript@5.8.3) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.2.3': + '@vitest/pretty-format@4.0.0-beta.8': dependencies: - '@vitest/utils': 3.2.3 + tinyrainbow: 2.0.0 + + '@vitest/runner@4.0.0-beta.8': + dependencies: + '@vitest/utils': 4.0.0-beta.8 pathe: 2.0.3 strip-literal: 3.0.0 - '@vitest/snapshot@3.2.3': + '@vitest/snapshot@3.2.4': dependencies: - '@vitest/pretty-format': 3.2.3 + '@vitest/pretty-format': 3.2.4 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.2.3': + '@vitest/snapshot@4.0.0-beta.8': + dependencies: + '@vitest/pretty-format': 4.0.0-beta.8 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': dependencies: tinyspy: 4.0.3 - '@vitest/utils@3.2.3': + '@vitest/spy@4.0.0-beta.8': {} + + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.2.3 - loupe: 3.1.3 + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.0 + tinyrainbow: 2.0.0 + + '@vitest/utils@4.0.0-beta.8': + dependencies: + '@vitest/pretty-format': 4.0.0-beta.8 + loupe: 3.2.0 tinyrainbow: 2.0.0 '@vscode/test-cli@0.0.6': @@ -6755,6 +7126,14 @@ snapshots: loupe: 3.1.3 pathval: 2.0.0 + chai@5.2.1: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.0 + pathval: 2.0.0 + chalk@3.0.0: dependencies: ansi-styles: 4.3.0 @@ -7604,7 +7983,7 @@ snapshots: expand-template@2.0.3: optional: true - expect-type@1.2.1: {} + expect-type@1.2.2: {} exsolve@1.0.5: {} @@ -7636,9 +8015,9 @@ snapshots: dependencies: pend: 1.2.0 - fdir@6.4.6(picomatch@4.0.2): + fdir@6.4.6(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 figures@6.1.0: dependencies: @@ -7976,7 +8355,7 @@ snapshots: ignore@7.0.5: {} - imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.2)(vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3): + imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8): dependencies: '@antfu/install-pkg': 0.1.1 chokidar: 3.6.0 @@ -7986,7 +8365,7 @@ snapshots: dotenv: 16.5.0 envinfo: 7.14.0 esbuild: 0.15.18 - fdir: 6.4.6(picomatch@4.0.2) + fdir: 6.4.6(picomatch@4.0.3) get-port: 5.1.1 local-pkg: 0.4.3 lodash.mergewith: 4.6.2 @@ -7994,9 +8373,9 @@ snapshots: optionalDependencies: '@testing-library/dom': 9.3.4 '@testing-library/jest-dom': 6.6.3 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vitest: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: - picomatch - supports-color @@ -8443,6 +8822,8 @@ snapshots: loupe@3.1.3: {} + loupe@3.2.0: {} + lru-cache@10.4.3: {} lru-cache@11.1.0: {} @@ -9210,8 +9591,14 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pirates@4.0.7: {} + pixelmatch@7.1.0: + dependencies: + pngjs: 7.0.0 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -9236,18 +9623,20 @@ snapshots: pluralize@8.0.0: {} + pngjs@7.0.0: {} + pnpm-workspace-yaml@0.3.1: dependencies: yaml: 2.8.0 possible-typed-array-names@1.1.0: {} - postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.4)(tsx@4.19.4)(yaml@2.8.0): + postcss-load-config@6.0.1(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(yaml@2.8.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 2.4.2 - postcss: 8.5.4 + postcss: 8.5.6 tsx: 4.19.4 yaml: 2.8.0 @@ -9262,6 +9651,12 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prebuild-install@7.1.3: dependencies: detect-libc: 2.0.4 @@ -9490,6 +9885,32 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.42.0 fsevents: 2.3.3 + rollup@4.46.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.46.2 + '@rollup/rollup-android-arm64': 4.46.2 + '@rollup/rollup-darwin-arm64': 4.46.2 + '@rollup/rollup-darwin-x64': 4.46.2 + '@rollup/rollup-freebsd-arm64': 4.46.2 + '@rollup/rollup-freebsd-x64': 4.46.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.46.2 + '@rollup/rollup-linux-arm-musleabihf': 4.46.2 + '@rollup/rollup-linux-arm64-gnu': 4.46.2 + '@rollup/rollup-linux-arm64-musl': 4.46.2 + '@rollup/rollup-linux-loongarch64-gnu': 4.46.2 + '@rollup/rollup-linux-ppc64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-gnu': 4.46.2 + '@rollup/rollup-linux-riscv64-musl': 4.46.2 + '@rollup/rollup-linux-s390x-gnu': 4.46.2 + '@rollup/rollup-linux-x64-gnu': 4.46.2 + '@rollup/rollup-linux-x64-musl': 4.46.2 + '@rollup/rollup-win32-arm64-msvc': 4.46.2 + '@rollup/rollup-win32-ia32-msvc': 4.46.2 + '@rollup/rollup-win32-x64-msvc': 4.46.2 + fsevents: 2.3.3 + rrweb-cssom@0.7.1: {} rrweb-cssom@0.8.0: {} @@ -9835,12 +10256,6 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - test-exclude@7.0.1: - dependencies: - '@istanbuljs/schema': 0.1.3 - glob: 10.4.5 - minimatch: 9.0.5 - text-table@0.2.0: {} textextensions@6.11.0: @@ -9877,10 +10292,10 @@ snapshots: tinyglobby@0.2.14: dependencies: - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 - tinypool@1.1.0: {} + tinypool@1.1.1: {} tinyrainbow@2.0.0: {} @@ -9940,7 +10355,7 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.0(@swc/core@1.3.107(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.4)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): + tsup@8.5.0(@swc/core@1.3.107(@swc/helpers@0.5.17))(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.8.0): dependencies: bundle-require: 5.1.0(esbuild@0.25.5) cac: 6.7.14 @@ -9951,7 +10366,7 @@ snapshots: fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.4)(tsx@4.19.4)(yaml@2.8.0) + postcss-load-config: 6.0.1(jiti@2.4.2)(postcss@8.5.6)(tsx@4.19.4)(yaml@2.8.0) resolve-from: 5.0.0 rollup: 4.42.0 source-map: 0.8.0-beta.0 @@ -9961,7 +10376,7 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: '@swc/core': 1.3.107(@swc/helpers@0.5.17) - postcss: 8.5.4 + postcss: 8.5.6 typescript: 5.8.3 transitivePeerDependencies: - jiti @@ -10078,13 +10493,13 @@ snapshots: version-range@4.14.0: {} - vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0): + vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1(supports-color@8.1.1) es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -10099,27 +10514,27 @@ snapshots: - tsx - yaml - vite-plugin-imba@0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.2)(vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)): + vite-plugin-imba@0.10.3(imba@2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)): dependencies: '@rollup/pluginutils': 4.2.1 cross-env: 7.0.3 debug: 4.4.1(supports-color@8.1.1) deepmerge: 4.3.1 diff: 5.2.0 - imba: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.2)(vite-node@3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + imba: 2.0.0-alpha.247(@testing-library/dom@9.3.4)(@testing-library/jest-dom@6.6.3)(picomatch@4.0.3)(vite-node@3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) kleur: 4.1.5 magic-string: 0.26.7 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) transitivePeerDependencies: - supports-color - vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0): + vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0): dependencies: esbuild: 0.25.5 - fdir: 6.4.6(picomatch@4.0.2) - picomatch: 4.0.2 - postcss: 8.5.4 - rollup: 4.42.0 + fdir: 6.4.6(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.46.2 tinyglobby: 0.2.14 optionalDependencies: '@types/node': 24.0.0 @@ -10128,41 +10543,86 @@ snapshots: tsx: 4.19.4 yaml: 2.8.0 - vitest-github-actions-reporter-temp@0.8.3(vitest@3.2.3): + vitest-github-actions-reporter-temp@0.8.3(vitest@4.0.0-beta.8): dependencies: '@actions/core': 1.11.1 source-map-js: 1.2.1 - vitest: 3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) + vitest: 4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0) - vitest@3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@14.7.1)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 - '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) - '@vitest/pretty-format': 3.2.3 - '@vitest/runner': 3.2.3 - '@vitest/snapshot': 3.2.3 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 - chai: 5.2.0 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 4.0.0-beta.8 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.2.1 debug: 4.4.1(supports-color@8.1.1) - expect-type: 1.2.1 + expect-type: 1.2.2 magic-string: 0.30.17 pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.14 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.12 + '@types/node': 24.0.0 + '@vitest/browser': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.4) + happy-dom: 15.11.7 + jsdom: 26.1.0 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vitest@4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@14.7.1)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 4.0.0-beta.8 + '@vitest/mocker': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 4.0.0-beta.8 + '@vitest/runner': 4.0.0-beta.8 + '@vitest/snapshot': 4.0.0-beta.8 + '@vitest/spy': 4.0.0-beta.8 + '@vitest/utils': 4.0.0-beta.8 + chai: 5.2.1 + debug: 4.4.1(supports-color@8.1.1) + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.17 + pathe: 2.0.3 + picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.1.0 + tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.0.0 - '@vitest/browser': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + '@vitest/browser': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) happy-dom: 14.7.1 jsdom: 26.1.0 transitivePeerDependencies: @@ -10179,35 +10639,35 @@ snapshots: - tsx - yaml - vitest@3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): + vitest@4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@24.1.3)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 - '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) - '@vitest/pretty-format': 3.2.3 - '@vitest/runner': 3.2.3 - '@vitest/snapshot': 3.2.3 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 - chai: 5.2.0 + '@vitest/expect': 4.0.0-beta.8 + '@vitest/mocker': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 4.0.0-beta.8 + '@vitest/runner': 4.0.0-beta.8 + '@vitest/snapshot': 4.0.0-beta.8 + '@vitest/spy': 4.0.0-beta.8 + '@vitest/utils': 4.0.0-beta.8 + chai: 5.2.1 debug: 4.4.1(supports-color@8.1.1) - expect-type: 1.2.1 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 magic-string: 0.30.17 pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.1.0 + tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.0.0 - '@vitest/browser': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + '@vitest/browser': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) happy-dom: 15.11.7 jsdom: 24.1.3 transitivePeerDependencies: @@ -10224,35 +10684,35 @@ snapshots: - tsx - yaml - vitest@3.2.3(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@3.2.3)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): + vitest@4.0.0-beta.8(@types/debug@4.1.12)(@types/node@24.0.0)(@vitest/browser@4.0.0-beta.8)(happy-dom@15.11.7)(jiti@2.4.2)(jsdom@26.1.0)(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(tsx@4.19.4)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 - '@vitest/expect': 3.2.3 - '@vitest/mocker': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) - '@vitest/pretty-format': 3.2.3 - '@vitest/runner': 3.2.3 - '@vitest/snapshot': 3.2.3 - '@vitest/spy': 3.2.3 - '@vitest/utils': 3.2.3 - chai: 5.2.0 + '@vitest/expect': 4.0.0-beta.8 + '@vitest/mocker': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0)) + '@vitest/pretty-format': 4.0.0-beta.8 + '@vitest/runner': 4.0.0-beta.8 + '@vitest/snapshot': 4.0.0-beta.8 + '@vitest/spy': 4.0.0-beta.8 + '@vitest/utils': 4.0.0-beta.8 + chai: 5.2.1 debug: 4.4.1(supports-color@8.1.1) - expect-type: 1.2.1 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 magic-string: 0.30.17 pathe: 2.0.3 - picomatch: 4.0.2 + picomatch: 4.0.3 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 - tinypool: 1.1.0 + tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) - vite-node: 3.2.3(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) + vite: 7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 '@types/node': 24.0.0 - '@vitest/browser': 3.2.3(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@6.3.5(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@3.2.3) + '@vitest/browser': 4.0.0-beta.8(msw@2.10.2(@types/node@24.0.0)(typescript@5.8.3))(playwright@1.52.0)(vite@7.1.2(@types/node@24.0.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.8.0))(vitest@4.0.0-beta.8) happy-dom: 15.11.7 jsdom: 26.1.0 transitivePeerDependencies: @@ -10397,6 +10857,8 @@ snapshots: ws@8.18.2: {} + ws@8.18.3: {} + xml-name-validator@4.0.0: {} xml-name-validator@5.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 9c702c3f..c0e317bd 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,13 +1,14 @@ packages: - ./ + - ./packages/* - samples/* - samples/monorepo-vitest-workspace/packages/* catalog: - '@vitest/browser': ^3.2.2 - '@vitest/coverage-istanbul': ^3.2.2 - '@vitest/coverage-v8': ^3.2.2 - '@vitest/runner': ^3.2.2 - '@vitest/utils': ^3.2.2 - vite: ^6.3.5 + '@vitest/browser': ^4.0.0-beta.8 + '@vitest/coverage-istanbul': ^4.0.0-beta.8 + '@vitest/coverage-v8': ^4.0.0-beta.8 + '@vitest/runner': ^4.0.0-beta.8 + '@vitest/utils': ^4.0.0-beta.8 + vite: ^7.1.0 vitest: ^3.2.2 diff --git a/samples/basic-v4/.skip/vitest.config.ts b/samples/basic-v4/.skip/vitest.config.ts new file mode 100644 index 00000000..91480683 --- /dev/null +++ b/samples/basic-v4/.skip/vitest.config.ts @@ -0,0 +1 @@ +throw new Error('do not import') \ No newline at end of file diff --git a/samples/basic-v4/.vscode/settings.json b/samples/basic-v4/.vscode/settings.json new file mode 100644 index 00000000..0842d6a8 --- /dev/null +++ b/samples/basic-v4/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "vitest.nodeEnv": { + "TEST_CUSTOM_ENV": "hello" + }, + "vitest.experimentalStaticAstCollect": true, + "[typescript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint" + } +} diff --git a/samples/basic-v4/package.json b/samples/basic-v4/package.json new file mode 100644 index 00000000..0291b284 --- /dev/null +++ b/samples/basic-v4/package.json @@ -0,0 +1,17 @@ +{ + "name": "basic-v4", + "version": "1.0.0", + "private": true, + "description": "", + "author": "", + "license": "ISC", + "scripts": { + "test": "vitest run" + }, + "devDependencies": { + "@vitest/browser": "catalog:", + "@vitest/coverage-v8": "catalog:", + "vite": "catalog:", + "vitest": "catalog:" + } +} diff --git a/samples/basic-v4/src/add.ts b/samples/basic-v4/src/add.ts new file mode 100644 index 00000000..a0e19156 --- /dev/null +++ b/samples/basic-v4/src/add.ts @@ -0,0 +1,17 @@ + +export function add(a: number, b: number) { + return a + b +} + +export function sum(from: number, to: number) { + return (from + to) * (to - from + 1) / 2 +} + +export function addError(from: number, to: number) { + doSomething() + return add(from, to) +} + +function doSomething() { + throw new Error('Something went wrong'); +} diff --git a/samples/basic-v4/src/should_included_test.ts b/samples/basic-v4/src/should_included_test.ts new file mode 100644 index 00000000..eb51c768 --- /dev/null +++ b/samples/basic-v4/src/should_included_test.ts @@ -0,0 +1,5 @@ +import { describe, it } from 'vitest' + +describe('should included', () => { + it('is included because of workspace plugin setting', () => {}) +}) diff --git a/samples/basic-v4/test/.gitignore b/samples/basic-v4/test/.gitignore new file mode 100644 index 00000000..b05c2dfa --- /dev/null +++ b/samples/basic-v4/test/.gitignore @@ -0,0 +1 @@ +__snapshots__ diff --git a/samples/basic-v4/test/add.test.ts b/samples/basic-v4/test/add.test.ts new file mode 100644 index 00000000..2517c278 --- /dev/null +++ b/samples/basic-v4/test/add.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from 'vitest' +import { add, sum } from '../src/add' + +describe('addition', () => { + it('add', () => { + expect(add(1, 1)).toBe(2) + }) + + it('sum', () => { + expect(sum(0, 10)).toBe(55) + }) + + it.skip('skipped', () => { + expect(1 + 2).toBe(3) + }) + + it.todo('todo') + it('async task', async () => { + await new Promise(resolve => setTimeout(resolve, 100)) + }) + + it('async task 0.5s', async () => { + await new Promise(resolve => setTimeout(resolve, 500)) + }) + + it('async task 1s', async () => { + await new Promise(resolve => setTimeout(resolve, 1000)) + }) + + it('long task', () => { + let sum = 0 + for (let i = 0; i < 2e8; i++) + sum += i + + expect(sum).toBeGreaterThan(1) + }) +}) + +describe('testing', () => { + it('run', () => { + const a = 10 + expect(a).toBe(10) + }) + + it('mul', () => { + expect(5 * 5).toBe(25) + }) + + it("mul fail", () => { + expect(5 * 5).toBe(25) + }) +}) diff --git a/samples/basic-v4/test/bug.test.ts b/samples/basic-v4/test/bug.test.ts new file mode 100644 index 00000000..aba9a462 --- /dev/null +++ b/samples/basic-v4/test/bug.test.ts @@ -0,0 +1,4 @@ +import { expect, test } from 'vitest'; +test('fail', () => { + expect(1).toEqual(2); +}) \ No newline at end of file diff --git a/samples/basic-v4/test/console.test.ts b/samples/basic-v4/test/console.test.ts new file mode 100644 index 00000000..4efe8f6a --- /dev/null +++ b/samples/basic-v4/test/console.test.ts @@ -0,0 +1,25 @@ +import { describe, it } from 'vitest' + +const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) + +describe('console', () => { + it('basic', () => { + console.log([ + 'string', + { hello: 'world' }, + 1234, + /regex/g, + true, + false, + null, + ]) + }) + + it('async', async () => { + console.log('1st') + await sleep(200) + console.log('2nd') + await sleep(200) + console.log('3rd') + }) +}) diff --git a/samples/basic-v4/test/deep/deeper/deep.test.ts b/samples/basic-v4/test/deep/deeper/deep.test.ts new file mode 100644 index 00000000..64456cdf --- /dev/null +++ b/samples/basic-v4/test/deep/deeper/deep.test.ts @@ -0,0 +1,5 @@ +import { expect, it } from 'vitest' + +it('test', () => { + expect(1).toBe(1) +}) \ No newline at end of file diff --git a/samples/basic-v4/test/duplicated.test.ts b/samples/basic-v4/test/duplicated.test.ts new file mode 100644 index 00000000..8f839ba0 --- /dev/null +++ b/samples/basic-v4/test/duplicated.test.ts @@ -0,0 +1,8 @@ +import { describe, test } from "vitest"; + +describe("testing", () => { + test("number 1", () => { }) +}); +describe("testing", () => { + test("number 2", () => { }) +}); diff --git a/samples/basic-v4/test/each.test.ts b/samples/basic-v4/test/each.test.ts new file mode 100644 index 00000000..f7cff915 --- /dev/null +++ b/samples/basic-v4/test/each.test.ts @@ -0,0 +1,84 @@ +import { describe, expect, it, test, } from 'vitest' + +describe('testing', (a) => { + it.each([ + [1, 1], [2, 2], [3, 3] + ])(`all pass: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + [1, 1], [2, 1], [3, 1] + ])(`first pass: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + [1, 1], [2, 2], [3, 1] + ])(`last pass: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + [1, 1], [2, 2], [3, 1] + ])(`first fail: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + [1, 1], [2, 2], [3, 1] + ])(`last fail: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + [1, 0], [2, 0], [3, 0] + ])(`all fail: %i => %i`, (a, b) => { + expect(a).toBe(b) + }) + it.each([ + 1, 2, 3 + ])('run %i', (a) => { + expect(a).toBe(a) + }) + it.each([ + [1, 1], [2, 4], [3, 9] + ])('run mul %i', (a,b) => { + expect(a * a).toBe(b) + }) + test.each([ + ["test1", 1], + ["test2", 2], + ["test3", 3], + ])(`%s => %i`, (a, b) => { + expect(a.at(-1)).toBe(`${b}`) + }) + test.each` + a | b | expected + ${1} | ${1} | ${2} + ${'a'} | ${'b'} | ${'ab'} + ${[]} | ${'b'} | ${'b'} + ${{}} | ${'b'} | ${'[object Object]b'} + ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} + `('table1: returns $expected when $a is added $b', ({ a, b, expected }) => { + expect(a + b).toBe(expected) + }) + test.each` + a | b | expected + ${{v: 1}} | ${{v: 1}} | ${2} + `('table2: returns $expected when $a.v is added $b.v', ({ a, b, expected }) => { + expect(a.v + b.v).toBe(expected) + }) + test.each([ + { input: 1, add: 1, sum: 2 }, + { input: 2, add: 2, sum: 4 }, + ])('$input + $add = $sum', ({ input, add, sum }) => { + expect(input + add).toBe(sum) + }) +}) + +// 'Test result not fourd' error occurs as both .each patterns are matched +// TODO: Fix this +describe("over matched test patterns", () => { + test.each(['1', '2'])('run %s', (a) => { + expect(a).toBe(String(a)) + }) + test.each(['1', '2'])('run for %s', (a) => { + expect(a).toBe(String(a)) + }) +}) diff --git a/samples/basic-v4/test/env.test.ts b/samples/basic-v4/test/env.test.ts new file mode 100644 index 00000000..07b88808 --- /dev/null +++ b/samples/basic-v4/test/env.test.ts @@ -0,0 +1,9 @@ +import { test, expect } from "vitest"; + +test('process.env', () => { + expect(process.env.TEST).toBe('true'); + expect(process.env.VITEST).toBe('true'); + expect(process.env.NODE_ENV).toBe('test'); + expect(process.env.VITEST_VSCODE).toBe('true'); + expect(process.env.TEST_CUSTOM_ENV).toBe('hello'); +}); diff --git a/samples/basic-v4/test/fail_to_run.test.ts b/samples/basic-v4/test/fail_to_run.test.ts new file mode 100644 index 00000000..13900e21 --- /dev/null +++ b/samples/basic-v4/test/fail_to_run.test.ts @@ -0,0 +1 @@ +test('aaaaaa', () => {}) diff --git a/samples/basic-v4/test/ignored.test.ts b/samples/basic-v4/test/ignored.test.ts new file mode 100644 index 00000000..ff1c9074 --- /dev/null +++ b/samples/basic-v4/test/ignored.test.ts @@ -0,0 +1,5 @@ +import { describe, it } from 'vitest' + +describe('ignored test', () => { + it('is ignored because of vitest plugin setting', () => {}) +}) diff --git a/samples/basic-v4/test/mul.test.ts b/samples/basic-v4/test/mul.test.ts new file mode 100644 index 00000000..b1a6e21e --- /dev/null +++ b/samples/basic-v4/test/mul.test.ts @@ -0,0 +1,6 @@ +import { describe, it } from 'vitest' + +describe('mul', () => { + it.skip('run 1', () => {}) + it('run', () => {}) +}) diff --git a/samples/basic-v4/test/snapshot.test.ts b/samples/basic-v4/test/snapshot.test.ts new file mode 100644 index 00000000..2944c559 --- /dev/null +++ b/samples/basic-v4/test/snapshot.test.ts @@ -0,0 +1,11 @@ +import { describe, expect, it } from 'vitest' + +describe('snapshots', () => { + it('string', () => { + expect('bc').toMatchSnapshot() + }) + it('async', async () => { + await new Promise(resolve => setTimeout(resolve, 200)) + expect('bc').toMatchSnapshot() + }) +}) diff --git a/samples/basic-v4/test/throw.test.ts b/samples/basic-v4/test/throw.test.ts new file mode 100644 index 00000000..73f8e547 --- /dev/null +++ b/samples/basic-v4/test/throw.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, it } from 'vitest'; +import { addError } from '../src/add'; + +describe('throw error', () => { + it('passes expecting an error to be thrown', () => { + expect(()=>addError(1, 1)).toThrow() + }) + + it('fails with error thrown', () => { + expect(addError(1, 1)).toBe(2) + }) +}) diff --git a/samples/basic-v4/test/using.test.ts b/samples/basic-v4/test/using.test.ts new file mode 100644 index 00000000..d7e13a31 --- /dev/null +++ b/samples/basic-v4/test/using.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from 'vitest'; + +(Symbol as any).dispose ??= Symbol('Symbol.dispose'); +(Symbol as any).asyncDispose ??= Symbol('Symbol.asyncDispose') + +describe('using keyword', () => { + it('dispose', () => { + function getDisposableResource() { + using resource = new SomeDisposableResource() + return resource + } + + const resource = getDisposableResource() + expect(resource.isDisposed).toBe(true) + }) + + it('asyncDispose', async () => { + async function getAsyncDisposableResource() { + await using resource = new SomeAsyncDisposableResource() + return resource + } + + const resource = await getAsyncDisposableResource() + expect(resource.isDisposed).toBe(true) + }) +}) + +class SomeDisposableResource implements Disposable { + public isDisposed = false; + + [Symbol.dispose](): void { + this.isDisposed = true + } +} + +class SomeAsyncDisposableResource implements AsyncDisposable { + public isDisposed = false + + async [Symbol.asyncDispose](): Promise { + await new Promise(resolve => setTimeout(resolve, 0)) + this.isDisposed = true + } +} diff --git a/samples/basic-v4/vite.config.ts b/samples/basic-v4/vite.config.ts new file mode 100644 index 00000000..c55daa96 --- /dev/null +++ b/samples/basic-v4/vite.config.ts @@ -0,0 +1,3 @@ +export default function () { + throw new Error('This file should not be executed') +} \ No newline at end of file diff --git a/samples/basic-v4/vitest.config.d.ts b/samples/basic-v4/vitest.config.d.ts new file mode 100644 index 00000000..5576c97e --- /dev/null +++ b/samples/basic-v4/vitest.config.d.ts @@ -0,0 +1,4 @@ +declare const ts: true + +// @ts-expect-error this is just a test +throw new Error('This file should not be included by default.') diff --git a/samples/basic-v4/vitest.config.ts b/samples/basic-v4/vitest.config.ts new file mode 100644 index 00000000..eeae7f53 --- /dev/null +++ b/samples/basic-v4/vitest.config.ts @@ -0,0 +1,15 @@ +/// + +// Configure Vitest (https://vitest.dev/config) + +import { defineConfig } from 'vite' + +export default defineConfig({ + esbuild: { + target: 'es2022', + }, + test: { + include: ['src/should_included_test.ts', 'test/**/*.test.ts'], + exclude: ['test/ignored.test.ts'], + }, +}) diff --git a/scripts/lower-vitest-version.js b/scripts/lower-vitest-version.js new file mode 100644 index 00000000..21e16b2a --- /dev/null +++ b/scripts/lower-vitest-version.js @@ -0,0 +1,11 @@ +const { readFileSync, writeFileSync } = require('node:fs') + +const pkg = JSON.parse(readFileSync('./package.json', 'utf-8')) +pkg.pnpm = { + overrides: { + '@vitest/browser': '^3.2.4', + '@vitest/coverage': '^3.2.4', + 'vitest': '^3.2.4', + }, +} +writeFileSync('./package.json', `${JSON.stringify(pkg, null, 2)}\n`, 'utf-8') diff --git a/src/api/rpc.ts b/src/api/rpc.ts deleted file mode 100644 index 0fae1442..00000000 --- a/src/api/rpc.ts +++ /dev/null @@ -1,129 +0,0 @@ -import type { RunnerTestFile, TaskResultPack, UserConsoleLog } from 'vitest' -import { stripVTControlCharacters } from 'node:util' -import v8 from 'node:v8' -import { type BirpcReturn, createBirpc } from 'birpc' -import { log } from '../log' - -export type SerializedTestSpecification = [ - project: { name: string | undefined }, - file: string, -] - -export interface ExtensionWorkerTransport { - getFiles: () => Promise<[project: string, file: string][]> - collectTests: (testFile: [project: string, filepath: string][]) => Promise - cancelRun: () => Promise - // accepts files with the project or folders (project doesn't matter for them) - runTests: (files?: SerializedTestSpecification[] | string[], testNamePattern?: string) => Promise - updateSnapshots: (files?: SerializedTestSpecification[] | string[], testNamePattern?: string) => Promise - - watchTests: (files?: SerializedTestSpecification[] | string[], testNamePattern?: string) => void - unwatchTests: () => void - - invalidateIstanbulTestModules: (modules: string[] | null) => Promise - enableCoverage: () => void - disableCoverage: () => void - waitForCoverageReport: () => Promise - close: () => void - - onFilesCreated: (files: string[]) => void - onFilesChanged: (files: string[]) => void -} - -export interface ExtensionWorkerEvents { - onConsoleLog: (log: UserConsoleLog) => void - onTaskUpdate: (task: TaskResultPack[]) => void - onFinished: (files: RunnerTestFile[], unhandledError: string, collecting?: boolean) => void - onCollected: (files?: RunnerTestFile[], collecting?: boolean) => void - onWatcherStart: (files?: RunnerTestFile[], errors?: unknown[], collecting?: boolean) => void - onWatcherRerun: (files: string[], trigger?: string, collecting?: boolean) => void - - onProcessLog: (type: 'stdout' | 'stderr', log: string) => void -} - -export type VitestRPC = BirpcReturn - -function createHandler any>() { - const handlers: T[] = [] - return { - handlers, - register: (listener: any) => handlers.push(listener), - trigger: (...data: any) => handlers.forEach(handler => handler(...data)), - clear: () => handlers.length = 0, - remove: (listener: T) => { - const index = handlers.indexOf(listener) - if (index !== -1) - handlers.splice(index, 1) - }, - } -} - -export function createRpcOptions() { - const handlers = { - onConsoleLog: createHandler(), - onTaskUpdate: createHandler(), - onFinished: createHandler(), - onCollected: createHandler(), - onWatcherRerun: createHandler(), - onWatcherStart: createHandler(), - } - - const events: Omit = { - onConsoleLog: handlers.onConsoleLog.trigger, - onFinished: handlers.onFinished.trigger, - onTaskUpdate: handlers.onTaskUpdate.trigger, - onCollected: handlers.onCollected.trigger, - onWatcherRerun: handlers.onWatcherRerun.trigger, - onWatcherStart: handlers.onWatcherStart.trigger, - onProcessLog(type, message) { - log.worker(type === 'stderr' ? 'error' : 'info', stripVTControlCharacters(message)) - }, - } - - return { - events, - handlers: { - onConsoleLog: handlers.onConsoleLog.register, - onTaskUpdate: handlers.onTaskUpdate.register, - onFinished: handlers.onFinished.register, - onCollected: handlers.onCollected.register, - onWatcherRerun: handlers.onWatcherRerun.register, - onWatcherStart: handlers.onWatcherStart.register, - removeListener(name: string, listener: any) { - handlers[name as 'onCollected']?.remove(listener) - }, - clearListeners() { - for (const name in handlers) - handlers[name as 'onCollected']?.clear() - }, - }, - } -} - -export function createVitestRpc(options: { - on: (listener: (message: any) => void) => void - send: (message: any) => void -}) { - const { events, handlers } = createRpcOptions() - - const api = createBirpc( - events, - { - timeout: -1, - bind: 'functions', - on(listener) { - options.on(listener) - }, - post(message) { - options.send(message) - }, - serialize: v8.serialize, - deserialize: v => v8.deserialize(Buffer.from(v) as any), - }, - ) - - return { - api, - handlers, - } -} diff --git a/src/worker/types.ts b/src/worker/types.ts deleted file mode 100644 index cbd74638..00000000 --- a/src/worker/types.ts +++ /dev/null @@ -1,48 +0,0 @@ -export interface WorkerInitMetadata { - vitestNodePath: string - id: string - cwd: string - arguments?: string - configFile?: string - workspaceFile?: string - env: Record | undefined - shellType: 'terminal' | 'child_process' - hasShellIntegration: boolean - pnpApi?: string - pnpLoader?: string -} - -export interface WorkerRunnerOptions { - type: 'init' - meta: WorkerInitMetadata - debug: boolean - astCollect: boolean -} - -export interface EventReady { - type: 'ready' - configs: string[] - workspaceSource: string | false -} - -export interface EventDebug { - type: 'debug' - args: string[] -} - -export interface EventError { - type: 'error' - error: string -} - -export type WorkerEvent = EventReady | EventDebug | EventError - -declare module 'vitest' { - export interface ProvidedContext { - __vscode: { - continuousFiles: string[] - watchEveryFile: boolean - rerunTriggered: boolean - } - } -} diff --git a/test/e2e/discovery.test.ts b/test/e2e/discovery.test.ts index 0309ec02..d3e449f6 100644 --- a/test/e2e/discovery.test.ts +++ b/test/e2e/discovery.test.ts @@ -2,7 +2,7 @@ import { resolve } from 'node:path' import type { RunnerTestCase, RunnerTestSuite } from 'vitest' import { describe, expect, it, onTestFinished } from 'vitest' import { createVitest } from 'vitest/node' -import { astCollectTests } from '../../src/worker/collect' +import { astCollectTests } from '../../packages/worker-legacy/src/collect' const variableFixture = 'test-from-vitest-variable.ts' @@ -15,7 +15,7 @@ describe('can discover tests', () => { const vitest = await createVitest('test', { config: false }) onTestFinished(() => vitest.close()) const file = await astCollectTests( - vitest.getCoreWorkspaceProject(), + vitest.getRootProject(), resolve(`test/e2e/fixtures/collect/${fixture}`), ) expect(file.filepath).toBe(resolve(`test/e2e/fixtures/collect/${fixture}`)) @@ -68,7 +68,7 @@ describe('can discover tests', () => { const vitest = await createVitest('test', { config: false }) onTestFinished(() => vitest.close()) const file = await astCollectTests( - vitest.getCoreWorkspaceProject(), + vitest.getRootProject(), resolve(`test/e2e/fixtures/collect/method-names.ts`), ) diff --git a/test/e2e/basic.test.ts b/test/e2e/runner.test.ts similarity index 98% rename from test/e2e/basic.test.ts rename to test/e2e/runner.test.ts index 5591fa69..8febc551 100644 --- a/test/e2e/basic.test.ts +++ b/test/e2e/runner.test.ts @@ -1,8 +1,8 @@ import { readFileSync, rmSync } from 'node:fs' import { beforeAll, beforeEach, describe, onTestFailed } from 'vitest' import { expect } from '@playwright/test' -import { test } from './helper' -import { editFile } from './tester' +import { test } from './utils/helper' +import { editFile } from './utils/tester' // Vitst extension doesn't work with CI flag beforeAll(() => { diff --git a/test/e2e/assertions.ts b/test/e2e/utils/assertions.ts similarity index 100% rename from test/e2e/assertions.ts rename to test/e2e/utils/assertions.ts diff --git a/test/e2e/downloadSetup.ts b/test/e2e/utils/downloadSetup.ts similarity index 100% rename from test/e2e/downloadSetup.ts rename to test/e2e/utils/downloadSetup.ts diff --git a/test/e2e/helper.ts b/test/e2e/utils/helper.ts similarity index 95% rename from test/e2e/helper.ts rename to test/e2e/utils/helper.ts index e99455ce..f424594e 100644 --- a/test/e2e/helper.ts +++ b/test/e2e/utils/helper.ts @@ -3,7 +3,6 @@ import os from 'node:os' import path, { resolve } from 'node:path' import { _electron } from '@playwright/test' import type { Page } from '@playwright/test' -import type { Awaitable } from 'vitest' import { test as baseTest, inject } from 'vitest' import { VSCodeTester } from './tester' @@ -20,7 +19,7 @@ type LaunchFixture = (options: { workspacePath?: string trace?: 'on' | 'off' }) => Promise Awaitable) => Promise + step: (name: string, fn: (context: Context) => void | Promise) => Promise }> const defaultConfig = process.env as { @@ -78,7 +77,7 @@ export const test = baseTest.extend<{ launch: LaunchFixture; taskName: string; l const tester = new VSCodeTester(page) - async function step(name: string, fn: (context: Context) => Awaitable) { + async function step(name: string, fn: (context: Context) => Promise | void) { await page.reload() try { await fn({ page, tester }) diff --git a/test/e2e/tester.ts b/test/e2e/utils/tester.ts similarity index 98% rename from test/e2e/tester.ts rename to test/e2e/utils/tester.ts index 00f0cbe1..a2b85a99 100644 --- a/test/e2e/tester.ts +++ b/test/e2e/utils/tester.ts @@ -57,7 +57,7 @@ class TesterTree { const state = await locator.getAttribute('aria-expanded') if (state === 'true') continue - await locator.click() + await locator.click({ force: true }) } } } diff --git a/test/e2e/vitest.config.ts b/test/e2e/vitest.config.ts index 003a1bfc..6f738863 100644 --- a/test/e2e/vitest.config.ts +++ b/test/e2e/vitest.config.ts @@ -10,10 +10,10 @@ export default defineConfig({ VSCODE_E2E_TRACE: 'on', }, setupFiles: [ - './assertions.ts', + './utils/assertions.ts', ], globalSetup: [ - './downloadSetup.ts', + './utils/downloadSetup.ts', ], }, }) diff --git a/test/unit/TestData.test.ts b/test/unit/TestData.test.ts index b32f237d..b8112214 100644 --- a/test/unit/TestData.test.ts +++ b/test/unit/TestData.test.ts @@ -1,7 +1,7 @@ import * as path from 'node:path' import * as vscode from 'vscode' import { expect } from 'chai' -import { TestCase, TestFile, TestFolder, TestSuite, getTestData } from '../../src/testTreeData' +import { TestCase, TestFile, TestFolder, TestSuite, getTestData } from '../../packages/extension/src/testTreeData' describe('TestData', () => { const ctrl = vscode.tests.createTestController('mocha', 'Vitest') diff --git a/test/unit/config.test.ts b/test/unit/config.test.ts index 05d5aec9..5aa6f55f 100644 --- a/test/unit/config.test.ts +++ b/test/unit/config.test.ts @@ -1,7 +1,7 @@ import { resolve } from 'node:path' import { homedir } from 'node:os' import { expect } from 'chai' -import { resolveConfigPath } from '../../src/config' +import { resolveConfigPath } from '../../packages/extension/src/config' it('correctly resolves ~', () => { expect(resolveConfigPath('~/test')).to.equal( diff --git a/test/unit/pkg.test.ts b/test/unit/pkg.test.ts index 7dc21aef..dcc16798 100644 --- a/test/unit/pkg.test.ts +++ b/test/unit/pkg.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai' -import { findFirstUniqueFolderNames } from '../../src/api/pkg' +import { findFirstUniqueFolderNames } from '../../packages/extension/src/api/pkg' it('correctly makes prefixes unique', () => { expect(findFirstUniqueFolderNames([ diff --git a/tsconfig.json b/tsconfig.json index 4b959e8f..422de379 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,4 @@ { "extends": ["./tsconfig.base.json"], - "include": ["src", "./debug-shims.d.ts"] + "include": ["src", "./debug-shims.d.ts", "packages/**/*"] } diff --git a/tsconfig.tsbuildinfo b/tsconfig.tsbuildinfo index be461f5c..ceda5908 100644 --- a/tsconfig.tsbuildinfo +++ b/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/api.ts","./src/config.ts","./src/constants.ts","./src/coverage.ts","./src/debug.ts","./src/diagnostic.ts","./src/extension.ts","./src/log.ts","./src/polyfills.ts","./src/runner.ts","./src/tagsmanager.ts","./src/testtree.ts","./src/testtreedata.ts","./src/utils.ts","./src/watcher.ts","./src/api/child_process.ts","./src/api/pkg.ts","./src/api/resolve.ts","./src/api/rpc.ts","./src/api/terminal.ts","./src/api/types.ts","./src/api/ws.ts","./src/worker/collect.ts","./src/worker/coverage.ts","./src/worker/emitter.ts","./src/worker/index.ts","./src/worker/init.ts","./src/worker/reporter.ts","./src/worker/rpc.ts","./src/worker/setupfile.ts","./src/worker/types.ts","./src/worker/utils.ts","./src/worker/watcher.ts","./src/worker/worker.ts","./debug-shims.d.ts"],"version":"5.8.3"} \ No newline at end of file +{"root":["./debug-shims.d.ts","./packages/extension/src/api.ts","./packages/extension/src/config.ts","./packages/extension/src/constants.ts","./packages/extension/src/coverage.ts","./packages/extension/src/debug.ts","./packages/extension/src/diagnostic.ts","./packages/extension/src/extension.ts","./packages/extension/src/log.ts","./packages/extension/src/polyfills.ts","./packages/extension/src/runner.ts","./packages/extension/src/tagsmanager.ts","./packages/extension/src/testtree.ts","./packages/extension/src/testtreedata.ts","./packages/extension/src/utils.ts","./packages/extension/src/watcher.ts","./packages/extension/src/api/child_process.ts","./packages/extension/src/api/pkg.ts","./packages/extension/src/api/resolve.ts","./packages/extension/src/api/rpc.ts","./packages/extension/src/api/terminal.ts","./packages/extension/src/api/types.ts","./packages/extension/src/api/ws.ts","./packages/extension/src/worker/index.ts","./packages/extension/src/worker/setupfile.ts","./packages/shared/src/emitter.ts","./packages/shared/src/index.ts","./packages/shared/src/rpc.ts","./packages/shared/src/utils.ts","./packages/worker/src/coverage.ts","./packages/worker/src/index.ts","./packages/worker/src/reporter.ts","./packages/worker/src/runner.ts","./packages/worker/src/watcher.ts","./packages/worker/src/worker.ts","./packages/worker-legacy/src/collect.ts","./packages/worker-legacy/src/coverage.ts","./packages/worker-legacy/src/index.ts","./packages/worker-legacy/src/reporter.ts","./packages/worker-legacy/src/types.ts","./packages/worker-legacy/src/watcher.ts","./packages/worker-legacy/src/worker.ts"],"version":"5.8.3"} \ No newline at end of file diff --git a/tsup.config.ts b/tsup.config.ts index 0c85244f..66b0bcae 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'tsup' export default defineConfig([ { - entry: ['./src/extension.ts'], + entry: ['./packages/extension/src/extension.ts'], external: ['vscode'], format: 'cjs', define: { @@ -11,12 +11,19 @@ export default defineConfig([ }, { entry: { - worker: './src/worker/index.ts', + worker: './packages/extension/src/worker/index.ts', }, format: 'cjs', }, { - entry: ['./src/worker/setupFile.ts'], + entry: { + workerLegacy: './packages/worker-legacy/src/index.ts', + workerNew: './packages/worker/src/index.ts', + }, + format: 'cjs', + }, + { + entry: ['./packages/extension/src/worker/setupFile.ts'], external: ['vitest'], format: 'esm', },