@vitest/browser 2.0.1 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.js ADDED
@@ -0,0 +1,333 @@
1
+ const DEFAULT_TIMEOUT = 6e4;
2
+ function defaultSerialize(i) {
3
+ return i;
4
+ }
5
+ const defaultDeserialize = defaultSerialize;
6
+ const { clearTimeout: clearTimeout$1, setTimeout: setTimeout$1 } = globalThis;
7
+ const random = Math.random.bind(Math);
8
+ function createBirpc(functions, options) {
9
+ const {
10
+ post,
11
+ on,
12
+ eventNames = [],
13
+ serialize = defaultSerialize,
14
+ deserialize = defaultDeserialize,
15
+ resolver,
16
+ timeout = DEFAULT_TIMEOUT
17
+ } = options;
18
+ const rpcPromiseMap = /* @__PURE__ */ new Map();
19
+ let _promise;
20
+ const rpc = new Proxy({}, {
21
+ get(_, method) {
22
+ if (method === "$functions")
23
+ return functions;
24
+ if (method === "then" && !eventNames.includes("then") && !("then" in functions))
25
+ return void 0;
26
+ const sendEvent = (...args) => {
27
+ post(serialize({ m: method, a: args, t: "q" }));
28
+ };
29
+ if (eventNames.includes(method)) {
30
+ sendEvent.asEvent = sendEvent;
31
+ return sendEvent;
32
+ }
33
+ const sendCall = async (...args) => {
34
+ await _promise;
35
+ return new Promise((resolve, reject) => {
36
+ const id = nanoid();
37
+ let timeoutId;
38
+ if (timeout >= 0) {
39
+ timeoutId = setTimeout$1(() => {
40
+ try {
41
+ options.onTimeoutError?.(method, args);
42
+ throw new Error(`[birpc] timeout on calling "${method}"`);
43
+ } catch (e) {
44
+ reject(e);
45
+ }
46
+ rpcPromiseMap.delete(id);
47
+ }, timeout);
48
+ if (typeof timeoutId === "object")
49
+ timeoutId = timeoutId.unref?.();
50
+ }
51
+ rpcPromiseMap.set(id, { resolve, reject, timeoutId });
52
+ post(serialize({ m: method, a: args, i: id, t: "q" }));
53
+ });
54
+ };
55
+ sendCall.asEvent = sendEvent;
56
+ return sendCall;
57
+ }
58
+ });
59
+ _promise = on(async (data, ...extra) => {
60
+ const msg = deserialize(data);
61
+ if (msg.t === "q") {
62
+ const { m: method, a: args } = msg;
63
+ let result, error;
64
+ const fn = resolver ? resolver(method, functions[method]) : functions[method];
65
+ if (!fn) {
66
+ error = new Error(`[birpc] function "${method}" not found`);
67
+ } else {
68
+ try {
69
+ result = await fn.apply(rpc, args);
70
+ } catch (e) {
71
+ error = e;
72
+ }
73
+ }
74
+ if (msg.i) {
75
+ if (error && options.onError)
76
+ options.onError(error, method, args);
77
+ post(serialize({ t: "s", i: msg.i, r: result, e: error }), ...extra);
78
+ }
79
+ } else {
80
+ const { i: ack, r: result, e: error } = msg;
81
+ const promise = rpcPromiseMap.get(ack);
82
+ if (promise) {
83
+ clearTimeout$1(promise.timeoutId);
84
+ if (error)
85
+ promise.reject(error);
86
+ else
87
+ promise.resolve(result);
88
+ }
89
+ rpcPromiseMap.delete(ack);
90
+ }
91
+ });
92
+ return rpc;
93
+ }
94
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
95
+ function nanoid(size = 21) {
96
+ let id = "";
97
+ let i = size;
98
+ while (i--)
99
+ id += urlAlphabet[random() * 64 | 0];
100
+ return id;
101
+ }
102
+
103
+ /// <reference types="../types/index.d.ts" />
104
+
105
+ // (c) 2020-present Andrea Giammarchi
106
+
107
+ const {parse: $parse, stringify: $stringify} = JSON;
108
+ const {keys} = Object;
109
+
110
+ const Primitive = String; // it could be Number
111
+ const primitive = 'string'; // it could be 'number'
112
+
113
+ const ignore = {};
114
+ const object = 'object';
115
+
116
+ const noop = (_, value) => value;
117
+
118
+ const primitives = value => (
119
+ value instanceof Primitive ? Primitive(value) : value
120
+ );
121
+
122
+ const Primitives = (_, value) => (
123
+ typeof value === primitive ? new Primitive(value) : value
124
+ );
125
+
126
+ const revive = (input, parsed, output, $) => {
127
+ const lazy = [];
128
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
129
+ const k = ke[y];
130
+ const value = output[k];
131
+ if (value instanceof Primitive) {
132
+ const tmp = input[value];
133
+ if (typeof tmp === object && !parsed.has(tmp)) {
134
+ parsed.add(tmp);
135
+ output[k] = ignore;
136
+ lazy.push({k, a: [input, parsed, tmp, $]});
137
+ }
138
+ else
139
+ output[k] = $.call(output, k, tmp);
140
+ }
141
+ else if (output[k] !== ignore)
142
+ output[k] = $.call(output, k, value);
143
+ }
144
+ for (let {length} = lazy, i = 0; i < length; i++) {
145
+ const {k, a} = lazy[i];
146
+ output[k] = $.call(output, k, revive.apply(null, a));
147
+ }
148
+ return output;
149
+ };
150
+
151
+ const set = (known, input, value) => {
152
+ const index = Primitive(input.push(value) - 1);
153
+ known.set(value, index);
154
+ return index;
155
+ };
156
+
157
+ /**
158
+ * Converts a specialized flatted string into a JS value.
159
+ * @param {string} text
160
+ * @param {(this: any, key: string, value: any) => any} [reviver]
161
+ * @returns {any}
162
+ */
163
+ const parse = (text, reviver) => {
164
+ const input = $parse(text, Primitives).map(primitives);
165
+ const value = input[0];
166
+ const $ = reviver || noop;
167
+ const tmp = typeof value === object && value ?
168
+ revive(input, new Set, value, $) :
169
+ value;
170
+ return $.call({'': tmp}, '', tmp);
171
+ };
172
+
173
+ /**
174
+ * Converts a JS value into a specialized flatted string.
175
+ * @param {any} value
176
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
177
+ * @param {string | number | undefined} [space]
178
+ * @returns {string}
179
+ */
180
+ const stringify = (value, replacer, space) => {
181
+ const $ = replacer && typeof replacer === object ?
182
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
183
+ (replacer || noop);
184
+ const known = new Map;
185
+ const input = [];
186
+ const output = [];
187
+ let i = +set(known, input, $.call({'': value}, '', value));
188
+ let firstRun = !i;
189
+ while (i < input.length) {
190
+ firstRun = true;
191
+ output[i] = $stringify(input[i++], replace, space);
192
+ }
193
+ return '[' + output.join(',') + ']';
194
+ function replace(key, value) {
195
+ if (firstRun) {
196
+ firstRun = !firstRun;
197
+ return value;
198
+ }
199
+ const after = $.call(this, key, value);
200
+ switch (typeof after) {
201
+ case object:
202
+ if (after === null) return after;
203
+ case primitive:
204
+ return known.get(after) || set(known, input, after);
205
+ }
206
+ return after;
207
+ }
208
+ };
209
+
210
+ // @__NO_SIDE_EFFECTS__
211
+ function getBrowserState() {
212
+ return window.__vitest_browser_runner__;
213
+ }
214
+
215
+ const channel = new BroadcastChannel(
216
+ `vitest:${getBrowserState().contextId}`
217
+ );
218
+ const globalChannel = new BroadcastChannel("vitest:global");
219
+ function waitForChannel(event) {
220
+ return new Promise((resolve) => {
221
+ channel.addEventListener(
222
+ "message",
223
+ (e) => {
224
+ if (e.data?.type === event) {
225
+ resolve();
226
+ }
227
+ },
228
+ { once: true }
229
+ );
230
+ });
231
+ }
232
+
233
+ const PAGE_TYPE = getBrowserState().type;
234
+ const PORT = location.port;
235
+ const HOST = [location.hostname, PORT].filter(Boolean).join(":");
236
+ const SESSION_ID = PAGE_TYPE === "orchestrator" ? getBrowserState().contextId : getBrowserState().testerId;
237
+ const ENTRY_URL = `${location.protocol === "https:" ? "wss:" : "ws:"}//${HOST}/__vitest_browser_api__?type=${PAGE_TYPE}&sessionId=${SESSION_ID}`;
238
+ let setCancel = (_) => {
239
+ };
240
+ const onCancel = new Promise((resolve) => {
241
+ setCancel = resolve;
242
+ });
243
+ function createClient() {
244
+ const reconnectInterval = 2e3;
245
+ const reconnectTries = 10;
246
+ const connectTimeout = 6e4;
247
+ let tries = reconnectTries;
248
+ const ctx = {
249
+ ws: new WebSocket(ENTRY_URL),
250
+ waitForConnection
251
+ };
252
+ let onMessage;
253
+ ctx.rpc = createBirpc(
254
+ {
255
+ onCancel: setCancel,
256
+ async createTesters(files) {
257
+ if (PAGE_TYPE !== "orchestrator") {
258
+ return;
259
+ }
260
+ getBrowserState().createTesters?.(files);
261
+ },
262
+ cdpEvent(event, payload) {
263
+ const cdp = getBrowserState().cdp;
264
+ if (!cdp) {
265
+ return;
266
+ }
267
+ cdp.emit(event, payload);
268
+ }
269
+ },
270
+ {
271
+ post: (msg) => ctx.ws.send(msg),
272
+ on: (fn) => onMessage = fn,
273
+ serialize: (e) => stringify(e, (_, v) => {
274
+ if (v instanceof Error) {
275
+ return {
276
+ name: v.name,
277
+ message: v.message,
278
+ stack: v.stack
279
+ };
280
+ }
281
+ return v;
282
+ }),
283
+ deserialize: parse,
284
+ onTimeoutError(functionName) {
285
+ throw new Error(`[vitest-browser]: Timeout calling "${functionName}"`);
286
+ }
287
+ }
288
+ );
289
+ let openPromise;
290
+ function reconnect(reset = false) {
291
+ if (reset) {
292
+ tries = reconnectTries;
293
+ }
294
+ ctx.ws = new WebSocket(ENTRY_URL);
295
+ registerWS();
296
+ }
297
+ function registerWS() {
298
+ openPromise = new Promise((resolve, reject) => {
299
+ const timeout = setTimeout(() => {
300
+ reject(
301
+ new Error(
302
+ `Cannot connect to the server in ${connectTimeout / 1e3} seconds`
303
+ )
304
+ );
305
+ }, connectTimeout)?.unref?.();
306
+ if (ctx.ws.OPEN === ctx.ws.readyState) {
307
+ resolve();
308
+ }
309
+ ctx.ws.addEventListener("open", () => {
310
+ tries = reconnectTries;
311
+ resolve();
312
+ clearTimeout(timeout);
313
+ });
314
+ });
315
+ ctx.ws.addEventListener("message", (v) => {
316
+ onMessage(v.data);
317
+ });
318
+ ctx.ws.addEventListener("close", () => {
319
+ tries -= 1;
320
+ if (tries > 0) {
321
+ setTimeout(reconnect, reconnectInterval);
322
+ }
323
+ });
324
+ }
325
+ registerWS();
326
+ function waitForConnection() {
327
+ return openPromise;
328
+ }
329
+ return ctx;
330
+ }
331
+ const client = createClient();
332
+
333
+ export { ENTRY_URL, HOST, PORT, SESSION_ID, channel, client, globalChannel, onCancel, waitForChannel };
package/dist/index.d.ts CHANGED
@@ -103,6 +103,7 @@ declare class BrowserServer implements BrowserServer$1 {
103
103
  testerHtml: Promise<string> | string;
104
104
  orchestratorHtml: Promise<string> | string;
105
105
  injectorJs: Promise<string> | string;
106
+ errorCatcherPath: Promise<string> | string;
106
107
  stateJs: Promise<string> | string;
107
108
  state: BrowserServerState;
108
109
  provider: BrowserProvider;
@@ -119,7 +120,7 @@ declare class BrowserServer implements BrowserServer$1 {
119
120
  initBrowserProvider(): Promise<void>;
120
121
  parseErrorStacktrace(e: ErrorWithDiff, options?: StackTraceParserOptions): vitest.ParsedStack[];
121
122
  parseStacktrace(trace: string, options?: StackTraceParserOptions): vitest.ParsedStack[];
122
- private cdpSessions;
123
+ private cdpSessionsPromises;
123
124
  ensureCDPHandler(contextId: string, sessionId: string): Promise<BrowserServerCDPHandler>;
124
125
  close(): Promise<void>;
125
126
  }
package/dist/index.js CHANGED
@@ -13,8 +13,8 @@ import { P as PlaywrightBrowserProvider, W as WebdriverBrowserProvider } from '.
13
13
  import { resolve as resolve$1, dirname as dirname$1, normalize as normalize$1 } from 'node:path';
14
14
  import MagicString from 'magic-string';
15
15
  import { esmWalker } from '@vitest/utils/ast';
16
- import * as nodeos from 'node:os';
17
16
  import crypto from 'node:crypto';
17
+ import * as nodeos from 'node:os';
18
18
 
19
19
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
20
20
  function normalizeWindowsPath(input = "") {
@@ -868,6 +868,7 @@ class BrowserServer {
868
868
  resolve(distRoot, "client/esm-client-injector.js"),
869
869
  "utf8"
870
870
  ).then((js) => this.injectorJs = js);
871
+ this.errorCatcherPath = resolve(distRoot, "client/error-catcher.js");
871
872
  this.stateJs = readFile$1(
872
873
  resolve(distRoot, "state.js"),
873
874
  "utf-8"
@@ -881,6 +882,7 @@ class BrowserServer {
881
882
  testerHtml;
882
883
  orchestratorHtml;
883
884
  injectorJs;
885
+ errorCatcherPath;
884
886
  stateJs;
885
887
  state;
886
888
  provider;
@@ -952,7 +954,7 @@ class BrowserServer {
952
954
  ...options
953
955
  });
954
956
  }
955
- cdpSessions = /* @__PURE__ */ new Map();
957
+ cdpSessionsPromises = /* @__PURE__ */ new Map();
956
958
  async ensureCDPHandler(contextId, sessionId) {
957
959
  const cachedHandler = this.state.cdps.get(sessionId);
958
960
  if (cachedHandler) {
@@ -962,11 +964,11 @@ class BrowserServer {
962
964
  if (!provider.getCDPSession) {
963
965
  throw new Error(`CDP is not supported by the provider "${provider.name}".`);
964
966
  }
965
- const promise = this.cdpSessions.get(sessionId) ?? await (async () => {
967
+ const promise = this.cdpSessionsPromises.get(sessionId) ?? await (async () => {
966
968
  const promise2 = provider.getCDPSession(contextId).finally(() => {
967
- this.cdpSessions.delete(sessionId);
969
+ this.cdpSessionsPromises.delete(sessionId);
968
970
  });
969
- this.cdpSessions.set(sessionId, promise2);
971
+ this.cdpSessionsPromises.set(sessionId, promise2);
970
972
  return promise2;
971
973
  })();
972
974
  const session = await promise;
@@ -1433,7 +1435,7 @@ const keyboard = async (context, text) => {
1433
1435
  throw new TypeError(`Provider "${context.provider.name}" does not support selecting all text`);
1434
1436
  }
1435
1437
  },
1436
- false
1438
+ true
1437
1439
  );
1438
1440
  };
1439
1441
  async function keyboardImplementation(provider, contextId, text, selectAll, skipRelease) {
@@ -1930,6 +1932,7 @@ async function resolveOrchestrator(server, url, res) {
1930
1932
  __VITEST_FILES__: JSON.stringify(files),
1931
1933
  __VITEST_TYPE__: '"orchestrator"',
1932
1934
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1935
+ __VITEST_TESTER_ID__: '"none"',
1933
1936
  __VITEST_PROVIDED_CONTEXT__: "{}"
1934
1937
  });
1935
1938
  res.removeHeader("Content-Security-Policy");
@@ -1946,7 +1949,8 @@ async function resolveOrchestrator(server, url, res) {
1946
1949
  baseHtml = baseHtml.replaceAll("./assets/", `${base}__vitest__/assets/`).replace(
1947
1950
  "<!-- !LOAD_METADATA! -->",
1948
1951
  [
1949
- "<script>{__VITEST_INJECTOR__}<\/script>",
1952
+ "{__VITEST_INJECTOR__}",
1953
+ "{__VITEST_ERROR_CATCHER__}",
1950
1954
  "{__VITEST_SCRIPTS__}",
1951
1955
  `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
1952
1956
  ].join("\n")
@@ -1956,7 +1960,8 @@ async function resolveOrchestrator(server, url, res) {
1956
1960
  __VITEST_FAVICON__: server.faviconUrl,
1957
1961
  __VITEST_TITLE__: "Vitest Browser Runner",
1958
1962
  __VITEST_SCRIPTS__: server.orchestratorScripts,
1959
- __VITEST_INJECTOR__: injector,
1963
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
1964
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherPath}"><\/script>`,
1960
1965
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId)
1961
1966
  });
1962
1967
  }
@@ -1989,6 +1994,7 @@ async function resolveTester(server, url, res) {
1989
1994
  }),
1990
1995
  __VITEST_TYPE__: '"tester"',
1991
1996
  __VITEST_CONTEXT_ID__: JSON.stringify(contextId),
1997
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
1992
1998
  __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(project.getProvidedContext()))
1993
1999
  });
1994
2000
  if (!server.testerScripts) {
@@ -2005,7 +2011,8 @@ async function resolveTester(server, url, res) {
2005
2011
  __VITEST_FAVICON__: server.faviconUrl,
2006
2012
  __VITEST_TITLE__: "Vitest Browser Tester",
2007
2013
  __VITEST_SCRIPTS__: server.testerScripts,
2008
- __VITEST_INJECTOR__: injector,
2014
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
2015
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${server.errorCatcherPath}"><\/script>`,
2009
2016
  __VITEST_APPEND__: `<script type="module">
2010
2017
  __vitest_browser_runner__.runningFiles = ${tests}
2011
2018
  __vitest_browser_runner__.iframeId = ${iframeId}
@@ -2018,7 +2025,6 @@ var BrowserPlugin = (browserServer, base = "/") => {
2018
2025
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2019
2026
  const distRoot = resolve(pkgRoot, "dist");
2020
2027
  const project = browserServer.project;
2021
- let loupePath;
2022
2028
  return [
2023
2029
  {
2024
2030
  enforce: "pre",
@@ -2144,6 +2150,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
2144
2150
  "vitest/utils",
2145
2151
  "vitest/browser",
2146
2152
  "vitest/runners",
2153
+ "@vitest/browser",
2154
+ "@vitest/browser/client",
2147
2155
  "@vitest/utils",
2148
2156
  "@vitest/utils/source-map",
2149
2157
  "@vitest/runner",
@@ -2154,21 +2162,15 @@ var BrowserPlugin = (browserServer, base = "/") => {
2154
2162
  "std-env",
2155
2163
  "tinybench",
2156
2164
  "tinyspy",
2165
+ "tinyrainbow",
2157
2166
  "pathe",
2158
2167
  "msw",
2159
2168
  "msw/browser"
2160
2169
  ],
2161
2170
  include: [
2162
- "vitest > @vitest/utils > pretty-format",
2163
- "vitest > @vitest/snapshot > pretty-format",
2164
2171
  "vitest > @vitest/snapshot > magic-string",
2165
- "vitest > pretty-format",
2166
- "vitest > pretty-format > ansi-styles",
2167
- "vitest > pretty-format > ansi-regex",
2168
2172
  "vitest > chai",
2169
2173
  "vitest > chai > loupe",
2170
- "vitest > @vitest/runner > pretty-format",
2171
- "vitest > @vitest/utils > diff-sequences",
2172
2174
  "vitest > @vitest/utils > loupe",
2173
2175
  "@vitest/browser > @testing-library/user-event",
2174
2176
  "@vitest/browser > @testing-library/dom"
@@ -2193,17 +2195,6 @@ var BrowserPlugin = (browserServer, base = "/") => {
2193
2195
  {
2194
2196
  name: "vitest:browser:resolve-virtual",
2195
2197
  async resolveId(rawId) {
2196
- if (rawId.startsWith("/__virtual_vitest__")) {
2197
- const url = new URL(rawId, "http://localhost");
2198
- if (!url.searchParams.has("id")) {
2199
- return;
2200
- }
2201
- const id = decodeURIComponent(url.searchParams.get("id"));
2202
- const resolved = await this.resolve(id, distRoot, {
2203
- skipSelf: true
2204
- });
2205
- return resolved;
2206
- }
2207
2198
  if (rawId === "/__vitest_msw__") {
2208
2199
  return this.resolve("msw/mockServiceWorker.js", distRoot, {
2209
2200
  skipSelf: true
@@ -2218,11 +2209,8 @@ var BrowserPlugin = (browserServer, base = "/") => {
2218
2209
  return resolve(distRoot, "client", id.slice(1));
2219
2210
  }
2220
2211
  },
2221
- configResolved(config) {
2222
- loupePath = resolve(config.cacheDir, "deps/loupe.js");
2223
- },
2224
2212
  transform(code, id) {
2225
- if (id.startsWith(loupePath)) {
2213
+ if (id.includes(browserServer.vite.config.cacheDir) && id.includes("loupe.js")) {
2226
2214
  const utilRequire = "nodeUtil = require_util();";
2227
2215
  return code.replace(utilRequire, " ".repeat(utilRequire.length));
2228
2216
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "2.0.1",
4
+ "version": "2.0.2",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -28,6 +28,9 @@
28
28
  "types": "./context.d.ts",
29
29
  "default": "./context.js"
30
30
  },
31
+ "./client": {
32
+ "default": "./dist/client.js"
33
+ },
31
34
  "./matchers": {
32
35
  "types": "./matchers.d.ts"
33
36
  },
@@ -51,7 +54,7 @@
51
54
  "peerDependencies": {
52
55
  "playwright": "*",
53
56
  "webdriverio": "*",
54
- "vitest": "2.0.1"
57
+ "vitest": "2.0.2"
55
58
  },
56
59
  "peerDependenciesMeta": {
57
60
  "playwright": {
@@ -71,7 +74,7 @@
71
74
  "msw": "^2.3.1",
72
75
  "sirv": "^2.0.4",
73
76
  "ws": "^8.17.1",
74
- "@vitest/utils": "2.0.1"
77
+ "@vitest/utils": "2.0.2"
75
78
  },
76
79
  "devDependencies": {
77
80
  "@testing-library/jest-dom": "^6.4.6",
@@ -85,10 +88,10 @@
85
88
  "playwright-core": "^1.45.0",
86
89
  "safaridriver": "^0.1.2",
87
90
  "webdriverio": "^8.39.0",
88
- "@vitest/runner": "2.0.1",
89
- "@vitest/ui": "2.0.1",
90
- "@vitest/ws-client": "2.0.1",
91
- "vitest": "2.0.1"
91
+ "@vitest/runner": "2.0.2",
92
+ "@vitest/ui": "2.0.2",
93
+ "@vitest/ws-client": "2.0.2",
94
+ "vitest": "2.0.2"
92
95
  },
93
96
  "scripts": {
94
97
  "build": "rimraf dist && pnpm build:node && pnpm build:client",