@vitest/browser 4.0.16 → 4.1.0-beta.1

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/index.js CHANGED
@@ -18,7 +18,7 @@ import { PNG } from 'pngjs';
18
18
  import pm from 'pixelmatch';
19
19
  import { WebSocketServer } from 'ws';
20
20
 
21
- var version = "4.0.16";
21
+ var version = "4.1.0-beta.1";
22
22
 
23
23
  const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
24
  function normalizeWindowsPath(input = "") {
@@ -695,6 +695,7 @@ async function resolveOrchestrator(globalServer, url, res) {
695
695
  __VITEST_TYPE__: "\"orchestrator\"",
696
696
  __VITEST_SESSION_ID__: JSON.stringify(sessionId),
697
697
  __VITEST_TESTER_ID__: "\"none\"",
698
+ __VITEST_OTEL_CARRIER__: url.searchParams.get("otelCarrier") ?? "null",
698
699
  __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(browserProject.project.getProvidedContext())),
699
700
  __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
700
701
  });
@@ -790,6 +791,7 @@ async function resolveTester(globalServer, url, res, next) {
790
791
  __VITEST_TYPE__: "\"tester\"",
791
792
  __VITEST_METHOD__: JSON.stringify("none"),
792
793
  __VITEST_SESSION_ID__: JSON.stringify(sessionId),
794
+ __VITEST_OTEL_CARRIER__: JSON.stringify(null),
793
795
  __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
794
796
  __VITEST_PROVIDED_CONTEXT__: "{}",
795
797
  __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
@@ -1073,7 +1075,6 @@ var BrowserPlugin = (parentServer, base = "/") => {
1073
1075
  "vitest",
1074
1076
  "vitest/browser",
1075
1077
  "vitest/internal/browser",
1076
- "vitest/runners",
1077
1078
  "vite/module-runner",
1078
1079
  "@vitest/browser/utils",
1079
1080
  "@vitest/browser/context",
@@ -1139,6 +1140,11 @@ var BrowserPlugin = (parentServer, base = "/") => {
1139
1140
  if (vueTestUtils) {
1140
1141
  include.push("@vue/test-utils");
1141
1142
  }
1143
+ const otelConfig = project.config.experimental.openTelemetry;
1144
+ if (otelConfig?.enabled && otelConfig.browserSdkPath) {
1145
+ entries.push(otelConfig.browserSdkPath);
1146
+ include.push("@opentelemetry/api");
1147
+ }
1142
1148
  return {
1143
1149
  define,
1144
1150
  resolve: { dedupe: ["vitest"] },
@@ -2224,9 +2230,20 @@ function asyncTimeout(timeout) {
2224
2230
  });
2225
2231
  }
2226
2232
 
2233
+ /**
2234
+ * Browser command that compares a screenshot against a stored reference.
2235
+ *
2236
+ * The comparison workflow is organized as follows:
2237
+ *
2238
+ * 1. Load existing reference (if any)
2239
+ * 2. Capture a stable screenshot (retrying until the page stops changing)
2240
+ * 3. Determine the outcome based on capture results and update settings
2241
+ * 4. Write any necessary files (new references, diffs)
2242
+ * 5. Return result for the test runner
2243
+ */
2227
2244
  const screenshotMatcher = async (context, name, testName, options) => {
2228
2245
  if (!context.testPath) {
2229
- throw new Error(`Cannot compare screenshots without a test path`);
2246
+ throw new Error("Cannot compare screenshots without a test path");
2230
2247
  }
2231
2248
  const { element } = options;
2232
2249
  const { codec, comparator, paths, resolvedOptions: { comparatorOptions, screenshotOptions, timeout } } = resolveOptions({
@@ -2236,9 +2253,8 @@ const screenshotMatcher = async (context, name, testName, options) => {
2236
2253
  options
2237
2254
  });
2238
2255
  const referenceFile = await readFile$1(paths.reference).catch(() => null);
2239
- const reference = referenceFile && await codec.decode(await readFile$1(paths.reference), {});
2240
- const abortController = new AbortController();
2241
- const stableScreenshot = getStableScreenshots({
2256
+ const reference = referenceFile && await codec.decode(referenceFile, {});
2257
+ const screenshotResult = await waitForStableScreenshot({
2242
2258
  codec,
2243
2259
  comparator,
2244
2260
  comparatorOptions,
@@ -2246,115 +2262,198 @@ const screenshotMatcher = async (context, name, testName, options) => {
2246
2262
  element,
2247
2263
  name: `${Date.now()}-${basename(paths.reference)}`,
2248
2264
  reference,
2249
- screenshotOptions,
2250
- signal: abortController.signal
2265
+ screenshotOptions
2266
+ }, timeout);
2267
+ const outcome = await determineOutcome({
2268
+ reference,
2269
+ screenshot: screenshotResult && screenshotResult.actual,
2270
+ retries: screenshotResult?.retries ?? 0,
2271
+ updateSnapshot: context.project.serializedConfig.snapshotOptions.updateSnapshot,
2272
+ paths,
2273
+ comparator,
2274
+ comparatorOptions
2251
2275
  });
2252
- const value = await (timeout === 0 ? stableScreenshot : Promise.race([stableScreenshot, asyncTimeout(timeout).finally(() => {
2253
- abortController.abort();
2254
- })]));
2255
- // case #01
2256
- // - impossible to get a stable screenshot to compare against
2257
- // - fail
2258
- if (value === null || value.actual === null) {
2276
+ await performSideEffects(outcome, codec);
2277
+ return buildOutput(outcome, timeout);
2278
+ };
2279
+ /**
2280
+ * Core comparison logic that produces a {@linkcode MatchOutcome}.
2281
+ *
2282
+ * All branching logic lives here. This is the single source of truth for "what happened".
2283
+ *
2284
+ * The outcome carries all data needed by {@linkcode performSideEffects} and {@linkcode buildOutput}.
2285
+ */
2286
+ async function determineOutcome({ comparator, comparatorOptions, paths, reference, retries, screenshot, updateSnapshot }) {
2287
+ if (screenshot === null) {
2259
2288
  return {
2260
- pass: false,
2261
- reference: referenceFile && {
2262
- path: paths.reference,
2263
- width: reference.metadata.width,
2264
- height: reference.metadata.height
2265
- },
2266
- actual: null,
2267
- diff: null,
2268
- message: `Could not capture a stable screenshot within ${timeout}ms.`
2289
+ type: "unstable-screenshot",
2290
+ reference: reference && {
2291
+ image: reference,
2292
+ path: paths.reference
2293
+ }
2269
2294
  };
2270
2295
  }
2271
- const { updateSnapshot } = context.project.serializedConfig.snapshotOptions;
2272
- // if there's no reference or if we want to update snapshots, we have to finish the comparison early
2273
- if (reference === null || updateSnapshot === "all") {
2274
- const shouldCreateReference = updateSnapshot !== "none";
2275
- const referencePath = shouldCreateReference ? paths.reference : paths.diffs.reference;
2276
- await writeScreenshot(referencePath, await codec.encode(value.actual, {}));
2277
- // case #02
2278
- // - got a stable screenshot, but there is no reference and we don't want to update screenshots
2279
- // - fail
2280
- if (updateSnapshot !== "all") {
2296
+ // no reference to compare against - create one based on update settings
2297
+ if (reference === null) {
2298
+ if (updateSnapshot === "all") {
2281
2299
  return {
2282
- pass: false,
2300
+ type: "update-reference",
2283
2301
  reference: {
2284
- path: referencePath,
2285
- width: value.actual.metadata.width,
2286
- height: value.actual.metadata.height
2287
- },
2288
- actual: null,
2289
- diff: null,
2290
- message: `No existing reference screenshot found${shouldCreateReference ? "; a new one was created. Review it before running tests again." : "."}`
2302
+ image: screenshot,
2303
+ path: paths.reference
2304
+ }
2291
2305
  };
2292
2306
  }
2293
- // case #03
2294
- // - got a stable screenshot, there is no reference, but we want to update screenshots
2295
- // - pass
2296
- return { pass: true };
2297
- }
2298
- // case #04
2299
- // - got a stable screenshot with no retries and there's a reference
2300
- // - pass
2301
- if (referenceFile && value.retries === 0) {
2302
- return { pass: true };
2303
- }
2304
- const finalResult = await comparator(reference, value.actual, {
2307
+ const location = updateSnapshot === "none" ? "diffs" : "reference";
2308
+ return {
2309
+ type: "missing-reference",
2310
+ location,
2311
+ reference: {
2312
+ image: screenshot,
2313
+ path: location === "reference" ? paths.reference : paths.diffs.reference
2314
+ }
2315
+ };
2316
+ }
2317
+ // first capture matched reference (used as baseline) - no further comparison needed
2318
+ if (retries === 0) {
2319
+ return { type: "matched-immediately" };
2320
+ }
2321
+ const comparisonResult = await comparator(reference, screenshot, {
2305
2322
  createDiff: true,
2306
2323
  ...comparatorOptions
2307
2324
  });
2308
- if (finalResult.pass === false && finalResult.diff !== null) {
2309
- const diff = await codec.encode({
2310
- data: finalResult.diff,
2311
- metadata: {
2312
- height: reference.metadata.height,
2313
- width: reference.metadata.width
2325
+ if (comparisonResult.pass) {
2326
+ return { type: "matched-after-comparison" };
2327
+ }
2328
+ if (updateSnapshot === "all") {
2329
+ return {
2330
+ type: "update-reference",
2331
+ reference: {
2332
+ image: screenshot,
2333
+ path: paths.reference
2314
2334
  }
2315
- }, {});
2316
- await writeScreenshot(paths.diffs.diff, diff);
2317
- }
2318
- // case #05
2319
- // - reference matches stable screenshot
2320
- // - pass
2321
- if (finalResult.pass === true) {
2322
- return { pass: true };
2323
- }
2324
- const actual = await codec.encode(value.actual, {});
2325
- await writeScreenshot(paths.diffs.actual, actual);
2326
- // case #06
2327
- // - fallback, reference does NOT match stable screenshot
2328
- // - fail
2335
+ };
2336
+ }
2329
2337
  return {
2330
- pass: false,
2338
+ type: "mismatch",
2331
2339
  reference: {
2332
- path: paths.reference,
2333
- width: reference.metadata.width,
2334
- height: reference.metadata.height
2340
+ image: reference,
2341
+ path: paths.reference
2335
2342
  },
2336
2343
  actual: {
2337
- path: paths.diffs.actual,
2338
- width: value.actual.metadata.width,
2339
- height: value.actual.metadata.height
2344
+ image: screenshot,
2345
+ path: paths.diffs.actual
2346
+ },
2347
+ diff: comparisonResult.diff && {
2348
+ image: {
2349
+ data: comparisonResult.diff,
2350
+ metadata: reference.metadata
2351
+ },
2352
+ path: paths.diffs.diff
2340
2353
  },
2341
- diff: finalResult.diff && paths.diffs.diff,
2342
- message: `Screenshot does not match the stored reference.${finalResult.message === null ? "" : `\n${finalResult.message}`}`
2354
+ message: comparisonResult.message
2343
2355
  };
2344
- };
2345
- async function writeScreenshot(path, image) {
2346
- try {
2347
- await mkdir(dirname(path), { recursive: true });
2348
- await writeFile$1(path, image);
2349
- } catch {
2350
- throw new Error("Couldn't write file to fs");
2356
+ }
2357
+ /**
2358
+ * Writes files to disk based on the outcome.
2359
+ *
2360
+ * Only `missing-reference`, `update-reference`, and `mismatch` write files. Successful matches produce no side effects.
2361
+ */
2362
+ async function performSideEffects(outcome, codec) {
2363
+ switch (outcome.type) {
2364
+ case "missing-reference":
2365
+ case "update-reference": {
2366
+ await writeScreenshot(outcome.reference.path, await codec.encode(outcome.reference.image, {}));
2367
+ break;
2368
+ }
2369
+ case "mismatch": {
2370
+ await writeScreenshot(outcome.actual.path, await codec.encode(outcome.actual.image, {}));
2371
+ if (outcome.diff) {
2372
+ await writeScreenshot(outcome.diff.path, await codec.encode(outcome.diff.image, {}));
2373
+ }
2374
+ break;
2375
+ }
2376
+ }
2377
+ }
2378
+ /**
2379
+ * Transforms a {@linkcode MatchOutcome} into the output format expected by the test runner.
2380
+ *
2381
+ * Maps each outcome to a pass/fail result with metadata and error messages.
2382
+ */
2383
+ function buildOutput(outcome, timeout) {
2384
+ switch (outcome.type) {
2385
+ case "unstable-screenshot": return {
2386
+ pass: false,
2387
+ reference: outcome.reference && {
2388
+ path: outcome.reference.path,
2389
+ width: outcome.reference.image.metadata.width,
2390
+ height: outcome.reference.image.metadata.height
2391
+ },
2392
+ actual: null,
2393
+ diff: null,
2394
+ message: `Could not capture a stable screenshot within ${timeout}ms.`
2395
+ };
2396
+ case "missing-reference": {
2397
+ return {
2398
+ pass: false,
2399
+ reference: {
2400
+ path: outcome.reference.path,
2401
+ width: outcome.reference.image.metadata.width,
2402
+ height: outcome.reference.image.metadata.height
2403
+ },
2404
+ actual: null,
2405
+ diff: null,
2406
+ message: outcome.location === "reference" ? "No existing reference screenshot found; a new one was created. Review it before running tests again." : "No existing reference screenshot found."
2407
+ };
2408
+ }
2409
+ case "update-reference":
2410
+ case "matched-immediately":
2411
+ case "matched-after-comparison": return { pass: true };
2412
+ case "mismatch": return {
2413
+ pass: false,
2414
+ reference: {
2415
+ path: outcome.reference.path,
2416
+ width: outcome.reference.image.metadata.width,
2417
+ height: outcome.reference.image.metadata.height
2418
+ },
2419
+ actual: {
2420
+ path: outcome.actual.path,
2421
+ width: outcome.actual.image.metadata.width,
2422
+ height: outcome.actual.image.metadata.height
2423
+ },
2424
+ diff: outcome.diff && {
2425
+ path: outcome.diff.path,
2426
+ width: outcome.diff.image.metadata.width,
2427
+ height: outcome.diff.image.metadata.height
2428
+ },
2429
+ message: `Screenshot does not match the stored reference.${outcome.message ? `\n${outcome.message}` : ""}`
2430
+ };
2431
+ default: {
2432
+ return {
2433
+ pass: false,
2434
+ actual: null,
2435
+ reference: null,
2436
+ diff: null,
2437
+ message: `Outcome (${outcome.type}) not handled. This is a bug in Vitest. Please, open an issue with reproduction.`
2438
+ };
2439
+ }
2351
2440
  }
2352
2441
  }
2353
2442
  /**
2443
+ * Captures a stable screenshot with timeout handling.
2444
+ *
2445
+ * Wraps {@linkcode getStableScreenshot} with an abort controller that triggers when the timeout expires. Returns `null` if the page never stabilizes.
2446
+ */
2447
+ async function waitForStableScreenshot(options, timeout) {
2448
+ const abortController = new AbortController();
2449
+ const stableScreenshot = getStableScreenshot(options, abortController.signal);
2450
+ const result = await (timeout === 0 ? stableScreenshot : Promise.race([stableScreenshot, asyncTimeout(timeout).finally(() => abortController.abort())]));
2451
+ return result;
2452
+ }
2453
+ /**
2354
2454
  * Takes screenshots repeatedly until the page reaches a visually stable state.
2355
2455
  *
2356
- * This function compares consecutive screenshots and continues taking new ones
2357
- * until two consecutive screenshots match according to the provided comparator.
2456
+ * This function compares consecutive screenshots and continues taking new ones until two consecutive screenshots match according to the provided comparator.
2358
2457
  *
2359
2458
  * The process works as follows:
2360
2459
  *
@@ -2364,10 +2463,9 @@ async function writeScreenshot(path, image) {
2364
2463
  * 4. If they don't match, it continues with the newer screenshot as the baseline
2365
2464
  * 5. Repeats until stability is achieved or the operation is aborted
2366
2465
  *
2367
- * @returns `Promise` resolving to an object containing the retry count and
2368
- * final screenshot
2466
+ * @returns `Promise` resolving to an object containing the retry count and final screenshot
2369
2467
  */
2370
- async function getStableScreenshots({ codec, context, comparator, comparatorOptions, element, name, reference, screenshotOptions, signal }) {
2468
+ async function getStableScreenshot({ codec, context, comparator, comparatorOptions, element, name, reference, screenshotOptions }, signal) {
2371
2469
  const screenshotArgument = {
2372
2470
  codec,
2373
2471
  context,
@@ -2382,12 +2480,12 @@ async function getStableScreenshots({ codec, context, comparator, comparatorOpti
2382
2480
  decodedBaseline = takeDecodedScreenshot(screenshotArgument);
2383
2481
  }
2384
2482
  const [image1, image2] = await Promise.all([decodedBaseline, takeDecodedScreenshot(screenshotArgument)]);
2385
- const comparatorResult = (await comparator(image1, image2, {
2483
+ const isStable = (await comparator(image1, image2, {
2386
2484
  ...comparatorOptions,
2387
2485
  createDiff: false
2388
2486
  })).pass;
2389
2487
  decodedBaseline = image2;
2390
- if (comparatorResult) {
2488
+ if (isStable) {
2391
2489
  break;
2392
2490
  }
2393
2491
  retries += 1;
@@ -2397,6 +2495,15 @@ async function getStableScreenshots({ codec, context, comparator, comparatorOpti
2397
2495
  actual: await decodedBaseline
2398
2496
  };
2399
2497
  }
2498
+ /** Writes encoded images to disk, creating parent directories as needed. */
2499
+ async function writeScreenshot(path, image) {
2500
+ try {
2501
+ await mkdir(dirname(path), { recursive: true });
2502
+ await writeFile$1(path, image);
2503
+ } catch (cause) {
2504
+ throw new Error("Couldn't write file to fs", { cause });
2505
+ }
2506
+ }
2400
2507
 
2401
2508
  var builtinCommands = {
2402
2509
  readFile,
@@ -2417,26 +2524,24 @@ class ProjectBrowser {
2417
2524
  testerFilepath;
2418
2525
  provider;
2419
2526
  vitest;
2527
+ vite;
2420
2528
  config;
2421
- children = new Set();
2422
- parent;
2423
2529
  state = new BrowserServerState();
2424
- constructor(project, base) {
2530
+ constructor(parent, project, base) {
2531
+ this.parent = parent;
2425
2532
  this.project = project;
2426
2533
  this.base = base;
2427
2534
  this.vitest = project.vitest;
2428
2535
  this.config = project.config;
2429
- const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
2430
- const distRoot = resolve(pkgRoot, "dist");
2536
+ this.vite = parent.vite;
2537
+ // instances can override testerHtmlPath
2431
2538
  const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2539
+ // TODO: when config resolution is rewritten, project and parentProject should be created before the vite server is started
2432
2540
  if (!existsSync(testerHtmlPath)) {
2433
2541
  throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2434
2542
  }
2435
2543
  this.testerFilepath = testerHtmlPath;
2436
- this.testerHtml = readFile$1(testerHtmlPath, "utf8").then((html) => this.testerHtml = html);
2437
- }
2438
- get vite() {
2439
- return this.parent.vite;
2544
+ this.testerHtml = readFile$1(this.testerFilepath, "utf8").then((html) => this.testerHtml = html);
2440
2545
  }
2441
2546
  commands = {};
2442
2547
  registerCommand(name, cb) {
@@ -2483,7 +2588,7 @@ class ProjectBrowser {
2483
2588
  return this.parent.parseStacktrace(trace, options);
2484
2589
  }
2485
2590
  async close() {
2486
- await this.parent.vite.close();
2591
+ await this.vite.close();
2487
2592
  }
2488
2593
  }
2489
2594
  function wrapConfig(config) {
@@ -2589,8 +2694,7 @@ class ParentBrowserProject {
2589
2694
  if (!this.vite) {
2590
2695
  throw new Error(`Cannot spawn child server without a parent dev server.`);
2591
2696
  }
2592
- const clone = new ProjectBrowser(project, "/");
2593
- clone.parent = this;
2697
+ const clone = new ProjectBrowser(this, project, "/");
2594
2698
  this.children.add(clone);
2595
2699
  return clone;
2596
2700
  }
@@ -3242,6 +3346,7 @@ const createBrowserServer = async (options) => {
3242
3346
  let cacheDir;
3243
3347
  const vite = await createViteServer({
3244
3348
  ...project.options,
3349
+ define: project.config.viteDefine,
3245
3350
  base: "/",
3246
3351
  root: project.config.root,
3247
3352
  logLevel,
@@ -1,4 +1,4 @@
1
- import { UserEventClickOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, LocatorByRoleOptions, LocatorOptions } from 'vitest/browser';
1
+ import { UserEventClickOptions, UserEventWheelOptions, UserEventClearOptions, UserEventHoverOptions, UserEventFillOptions, UserEventUploadOptions, UserEventDragAndDropOptions, UserEventSelectOptions, LocatorScreenshotOptions, LocatorByRoleOptions, LocatorOptions } from 'vitest/browser';
2
2
 
3
3
  type ClauseCombinator = '' | '>' | '+' | '~' | '>=';
4
4
  type CSSFunctionArgument = CSSComplexSelector | number | string;
@@ -312,6 +312,7 @@ declare abstract class Locator {
312
312
  click(options?: UserEventClickOptions): Promise<void>;
313
313
  dblClick(options?: UserEventClickOptions): Promise<void>;
314
314
  tripleClick(options?: UserEventClickOptions): Promise<void>;
315
+ wheel(options: UserEventWheelOptions): Promise<void>;
315
316
  clear(options?: UserEventClearOptions): Promise<void>;
316
317
  hover(options?: UserEventHoverOptions): Promise<void>;
317
318
  unhover(options?: UserEventHoverOptions): Promise<void>;
package/dist/locators.js CHANGED
@@ -1 +1 @@
1
- export{L as Locator,n as convertElementToCssSelector,q as getByAltTextSelector,r as getByLabelSelector,t as getByPlaceholderSelector,u as getByRoleSelector,v as getByTestIdSelector,w as getByTextSelector,x as getByTitleSelector,o as getIframeScale,p as processTimeoutOptions,s as selectorEngine}from"./index-CEutxZap.js";import"vitest/browser";import"vitest/internal/browser";
1
+ export{L as Locator,n as convertElementToCssSelector,q as getByAltTextSelector,r as getByLabelSelector,t as getByPlaceholderSelector,u as getByRoleSelector,v as getByTestIdSelector,w as getByTextSelector,x as getByTitleSelector,o as getIframeScale,p as processTimeoutOptions,s as selectorEngine}from"./index-5ZLDAkrH.js";import"vitest/browser";import"vitest/internal/browser";
@@ -14,7 +14,7 @@ export type ScreenshotMatcherOutput = Promise<{
14
14
  pass: false;
15
15
  reference: ScreenshotData | null;
16
16
  actual: ScreenshotData | null;
17
- diff: string | null;
17
+ diff: ScreenshotData | null;
18
18
  message: string;
19
19
  } | {
20
20
  pass: true;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "4.0.16",
4
+ "version": "4.1.0-beta.1",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -51,7 +51,7 @@
51
51
  "providers"
52
52
  ],
53
53
  "peerDependencies": {
54
- "vitest": "4.0.16"
54
+ "vitest": "4.1.0-beta.1"
55
55
  },
56
56
  "dependencies": {
57
57
  "magic-string": "^0.30.21",
@@ -59,21 +59,22 @@
59
59
  "pngjs": "^7.0.0",
60
60
  "sirv": "^3.0.2",
61
61
  "tinyrainbow": "^3.0.3",
62
- "ws": "^8.18.3",
63
- "@vitest/mocker": "4.0.16",
64
- "@vitest/utils": "4.0.16"
62
+ "ws": "^8.19.0",
63
+ "@vitest/mocker": "4.1.0-beta.1",
64
+ "@vitest/utils": "4.1.0-beta.1"
65
65
  },
66
66
  "devDependencies": {
67
+ "@opentelemetry/api": "^1.9.0",
67
68
  "@testing-library/user-event": "^14.6.1",
68
69
  "@types/pngjs": "^6.0.5",
69
70
  "@types/ws": "^8.18.1",
70
71
  "birpc": "^4.0.0",
71
- "flatted": "^3.3.3",
72
+ "flatted": "3.3.3",
72
73
  "ivya": "^1.7.0",
73
74
  "mime": "^4.1.0",
74
75
  "pathe": "^2.0.3",
75
- "@vitest/runner": "4.0.16",
76
- "vitest": "4.0.16"
76
+ "@vitest/runner": "4.1.0-beta.1",
77
+ "vitest": "4.1.0-beta.1"
77
78
  },
78
79
  "scripts": {
79
80
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",
@@ -81,7 +82,7 @@
81
82
  "build": "premove dist && pnpm build:node && pnpm build:client",
82
83
  "build:client": "vite build src/client",
83
84
  "build:node": "rollup -c",
84
- "dev:client": "vite build src/client --watch",
85
+ "dev:client": "node --watch-preserve-output --watch-path src/client scripts/build-client.js",
85
86
  "dev:node": "rollup -c --watch --watch.include 'src/**'",
86
87
  "dev": "premove dist && pnpm run --stream '/^dev:/'"
87
88
  }