@cloudflare/vitest-pool-workers 0.2.3 → 0.2.4

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.
Files changed (54) hide show
  1. package/package.json +2 -2
  2. package/dist/config/d1.d.ts +0 -7
  3. package/dist/config/index.cjs +0 -130
  4. package/dist/config/index.cjs.map +0 -6
  5. package/dist/config/index.d.ts +0 -21
  6. package/dist/pool/config.d.ts +0 -82
  7. package/dist/pool/helpers.d.ts +0 -5
  8. package/dist/pool/index.mjs +0 -1529
  9. package/dist/pool/index.mjs.map +0 -6
  10. package/dist/shared/d1.d.ts +0 -4
  11. package/dist/worker/index.mjs +0 -352
  12. package/dist/worker/index.mjs.map +0 -6
  13. package/dist/worker/lib/cloudflare/empty-internal.cjs +0 -27
  14. package/dist/worker/lib/cloudflare/empty-internal.cjs.map +0 -6
  15. package/dist/worker/lib/cloudflare/mock-agent.cjs +0 -2056
  16. package/dist/worker/lib/cloudflare/mock-agent.cjs.map +0 -6
  17. package/dist/worker/lib/cloudflare/test-internal.mjs +0 -739
  18. package/dist/worker/lib/cloudflare/test-internal.mjs.map +0 -6
  19. package/dist/worker/lib/cloudflare/test-runner.mjs +0 -222
  20. package/dist/worker/lib/cloudflare/test-runner.mjs.map +0 -6
  21. package/dist/worker/lib/cloudflare/test.mjs +0 -30
  22. package/dist/worker/lib/cloudflare/test.mjs.map +0 -6
  23. package/dist/worker/lib/debug.mjs +0 -9
  24. package/dist/worker/lib/debug.mjs.map +0 -6
  25. package/dist/worker/lib/mlly.mjs +0 -48
  26. package/dist/worker/lib/mlly.mjs.map +0 -6
  27. package/dist/worker/lib/node/console.mjs +0 -104
  28. package/dist/worker/lib/node/console.mjs.map +0 -6
  29. package/dist/worker/lib/node/dns.mjs +0 -6
  30. package/dist/worker/lib/node/dns.mjs.map +0 -6
  31. package/dist/worker/lib/node/fs/promises.mjs +0 -6
  32. package/dist/worker/lib/node/fs/promises.mjs.map +0 -6
  33. package/dist/worker/lib/node/fs.mjs +0 -25
  34. package/dist/worker/lib/node/fs.mjs.map +0 -6
  35. package/dist/worker/lib/node/http.cjs +0 -97
  36. package/dist/worker/lib/node/http.cjs.map +0 -6
  37. package/dist/worker/lib/node/module.mjs +0 -15
  38. package/dist/worker/lib/node/module.mjs.map +0 -6
  39. package/dist/worker/lib/node/net.cjs +0 -27
  40. package/dist/worker/lib/node/net.cjs.map +0 -6
  41. package/dist/worker/lib/node/perf_hooks.mjs +0 -6
  42. package/dist/worker/lib/node/perf_hooks.mjs.map +0 -6
  43. package/dist/worker/lib/node/querystring.cjs +0 -44
  44. package/dist/worker/lib/node/querystring.cjs.map +0 -6
  45. package/dist/worker/lib/node/timers.mjs +0 -6
  46. package/dist/worker/lib/node/timers.mjs.map +0 -6
  47. package/dist/worker/lib/node/tty.mjs +0 -8
  48. package/dist/worker/lib/node/tty.mjs.map +0 -6
  49. package/dist/worker/lib/node/url.mjs +0 -75
  50. package/dist/worker/lib/node/url.mjs.map +0 -6
  51. package/dist/worker/lib/node/vm.mjs +0 -17
  52. package/dist/worker/lib/node/vm.mjs.map +0 -6
  53. package/dist/worker/lib/tinypool.mjs +0 -6
  54. package/dist/worker/lib/tinypool.mjs.map +0 -6
@@ -1,1529 +0,0 @@
1
- // <define:VITEST_POOL_WORKERS_DEFINE_BUILTIN_MODULES>
2
- var define_VITEST_POOL_WORKERS_DEFINE_BUILTIN_MODULES_default = ["workerd:compatibility-flags", "node-internal:async_hooks", "node-internal:buffer", "node-internal:crypto", "node-internal:util", "node-internal:diagnostics_channel", "node:_stream_duplex", "node:_stream_passthrough", "node:_stream_readable", "node:_stream_transform", "node:_stream_writable", "node:assert", "node:async_hooks", "node:buffer", "node:crypto", "node:diagnostics_channel", "node:events", "node:path", "node:process", "node:stream", "node:stream/consumers", "node:stream/promises", "node:stream/web", "node:string_decoder", "node:test", "node:util", "node-internal:constants", "node-internal:crypto_dh", "node-internal:crypto_hash", "node-internal:crypto_hkdf", "node-internal:crypto_keys", "node-internal:crypto_pbkdf2", "node-internal:crypto_random", "node-internal:crypto_util", "node-internal:debuglog", "node-internal:events", "node-internal:internal_assert", "node-internal:internal_assertionerror", "node-internal:internal_buffer", "node-internal:internal_comparisons", "node-internal:internal_diffs", "node-internal:internal_errors", "node-internal:internal_inspect", "node-internal:internal_path", "node-internal:internal_stringdecoder", "node-internal:internal_types", "node-internal:internal_utils", "node-internal:mock", "node-internal:process", "node-internal:streams_adapters", "node-internal:streams_compose", "node-internal:streams_duplex", "node-internal:streams_legacy", "node-internal:streams_pipeline", "node-internal:streams_promises", "node-internal:streams_readable", "node-internal:streams_transform", "node-internal:streams_util", "node-internal:streams_writable", "node-internal:validators", "internal:unsafe-eval", "cloudflare-internal:sockets", "cloudflare:ai", "cloudflare:email", "cloudflare:sockets", "cloudflare:vectorize", "cloudflare:workers", "cloudflare-internal:ai-api", "cloudflare-internal:d1-api", "cloudflare-internal:vectorize-api", "cloudflare-internal:workers", "workerd:unsafe"];
3
-
4
- // src/pool/index.ts
5
- import assert4 from "node:assert";
6
- import crypto from "node:crypto";
7
- import events from "node:events";
8
- import fs3 from "node:fs";
9
- import path4 from "node:path";
10
- import { fileURLToPath as fileURLToPath2 } from "node:url";
11
- import util2 from "node:util";
12
- import { createBirpc } from "birpc";
13
- import * as devalue from "devalue";
14
- import {
15
- compileModuleRules,
16
- kCurrentWorker,
17
- kUnsafeEphemeralUniqueKey,
18
- Log,
19
- LogLevel,
20
- maybeApply,
21
- Miniflare,
22
- structuredSerializableReducers,
23
- structuredSerializableRevivers,
24
- testRegExps,
25
- WebSocket
26
- } from "miniflare";
27
- import { createMethodsRPC } from "vitest/node";
28
-
29
- // src/shared/chunking-socket.ts
30
- import assert from "node:assert";
31
- import { Buffer } from "node:buffer";
32
- function createChunkingSocket(socket, maxChunkByteLength = 1048576) {
33
- const listeners = [];
34
- const decoder = new TextDecoder();
35
- let chunks;
36
- socket.on((message) => {
37
- if (typeof message === "string") {
38
- if (chunks !== void 0) {
39
- assert.strictEqual(message, "", "Expected end-of-chunks");
40
- message = chunks + decoder.decode();
41
- chunks = void 0;
42
- }
43
- for (const listener of listeners)
44
- listener(message);
45
- } else {
46
- chunks ??= "";
47
- chunks += decoder.decode(message, { stream: true });
48
- }
49
- });
50
- return {
51
- post(value) {
52
- if (Buffer.byteLength(value) > maxChunkByteLength) {
53
- const encoded = Buffer.from(value);
54
- for (let i = 0; i < encoded.byteLength; i += maxChunkByteLength) {
55
- socket.post(encoded.subarray(i, i + maxChunkByteLength));
56
- }
57
- socket.post("");
58
- } else {
59
- socket.post(value);
60
- }
61
- },
62
- on(listener) {
63
- listeners.push(listener);
64
- }
65
- };
66
- }
67
-
68
- // src/pool/config.ts
69
- import path2 from "node:path";
70
- import {
71
- formatZodError,
72
- getRootPath,
73
- mergeWorkerOptions,
74
- parseWithRootPath,
75
- PLUGINS
76
- } from "miniflare";
77
- import { z } from "zod";
78
-
79
- // src/pool/helpers.ts
80
- import path from "node:path";
81
- var WORKER_NAME_PREFIX = "vitest-pool-workers-";
82
- function isFileNotFoundError(e) {
83
- return typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT";
84
- }
85
- function getProjectPath(project) {
86
- return project.config.config ?? project.path;
87
- }
88
- function getRelativeProjectPath(project) {
89
- const projectPath = getProjectPath(project);
90
- if (typeof projectPath === "number")
91
- return projectPath;
92
- else
93
- return path.relative("", projectPath);
94
- }
95
-
96
- // src/pool/config.ts
97
- var PLUGIN_VALUES = Object.values(PLUGINS);
98
- var OPTIONS_PATH_ARRAY = ["test", "poolOptions", "workers"];
99
- var OPTIONS_PATH = OPTIONS_PATH_ARRAY.join(".");
100
- var WorkersPoolOptionsSchema = z.object({
101
- /**
102
- * Entrypoint to Worker run in the same isolate/context as tests. This is
103
- * required to use `import { SELF } from "cloudflare:test"`, or Durable
104
- * Objects without an explicit `scriptName`. Note this goes through Vite
105
- * transforms and can be a TypeScript file. Note also
106
- * `import module from "<path-to-main>"` inside tests gives exactly the same
107
- * `module` instance as is used internally for the `SELF` and Durable Object
108
- * bindings.
109
- */
110
- main: z.ostring(),
111
- /**
112
- * Enables per-test isolated storage. If enabled, any writes to storage
113
- * performed in a test will be undone at the end of the test. The test storage
114
- * environment is copied from the containing suite, meaning `beforeAll()`
115
- * hooks can be used to seed data. If this is disabled, all tests will share
116
- * the same storage.
117
- */
118
- isolatedStorage: z.boolean().default(true),
119
- /**
120
- * Runs all tests in this project serially in the same worker, using the same
121
- * module cache. This can significantly speed up tests if you've got lots of
122
- * small test files.
123
- */
124
- singleWorker: z.boolean().default(false),
125
- miniflare: z.object({
126
- workers: z.array(z.object({}).passthrough()).optional()
127
- }).passthrough().optional(),
128
- wrangler: z.object({ configPath: z.ostring(), environment: z.ostring() }).optional()
129
- });
130
- function isZodErrorLike(value) {
131
- return typeof value === "object" && value !== null && "issues" in value && Array.isArray(value.issues);
132
- }
133
- function coalesceZodErrors(ref, thrown) {
134
- if (!isZodErrorLike(thrown))
135
- throw thrown;
136
- if (ref.value === void 0)
137
- ref.value = thrown;
138
- else
139
- ref.value.issues.push(...thrown.issues);
140
- }
141
- function parseWorkerOptions(rootPath, value, withoutScript, opts) {
142
- if (withoutScript) {
143
- value["script"] = "";
144
- delete value["scriptPath"];
145
- delete value["modules"];
146
- delete value["modulesRoot"];
147
- }
148
- const result = {};
149
- const errorRef = {};
150
- for (const plugin of PLUGIN_VALUES) {
151
- try {
152
- const parsed = parseWithRootPath(rootPath, plugin.options, value, opts);
153
- Object.assign(result, parsed);
154
- } catch (e) {
155
- coalesceZodErrors(errorRef, e);
156
- }
157
- }
158
- if (errorRef.value !== void 0)
159
- throw errorRef.value;
160
- if (withoutScript)
161
- delete value["script"];
162
- return result;
163
- }
164
- async function parseCustomPoolOptions(rootPath, value, opts) {
165
- const options = WorkersPoolOptionsSchema.parse(
166
- value,
167
- opts
168
- );
169
- options.miniflare ??= {};
170
- const errorRef = {};
171
- const workers = options.miniflare?.workers;
172
- const rootPathOption = getRootPath(options.miniflare);
173
- rootPath = path2.resolve(rootPath, rootPathOption);
174
- try {
175
- options.miniflare = parseWorkerOptions(
176
- rootPath,
177
- options.miniflare,
178
- /* withoutScript */
179
- true,
180
- // (script provided by runner)
181
- { path: [...opts.path, "miniflare"] }
182
- );
183
- } catch (e) {
184
- coalesceZodErrors(errorRef, e);
185
- }
186
- if (workers !== void 0) {
187
- options.miniflare.workers = workers.map((worker, i) => {
188
- try {
189
- const workerRootPathOption = getRootPath(worker);
190
- const workerRootPath = path2.resolve(rootPath, workerRootPathOption);
191
- return parseWorkerOptions(
192
- workerRootPath,
193
- worker,
194
- /* withoutScript */
195
- false,
196
- {
197
- path: [...opts.path, "miniflare", "workers", i]
198
- }
199
- );
200
- } catch (e) {
201
- coalesceZodErrors(errorRef, e);
202
- return { script: "" };
203
- }
204
- });
205
- }
206
- if (errorRef.value !== void 0)
207
- throw errorRef.value;
208
- if (options.wrangler?.configPath !== void 0) {
209
- const configPath = path2.resolve(rootPath, options.wrangler.configPath);
210
- options.wrangler.configPath = configPath;
211
- const wrangler = await import("wrangler");
212
- const { workerOptions, define, main } = wrangler.unstable_getMiniflareWorkerOptions(
213
- configPath,
214
- options.wrangler.environment
215
- );
216
- options.main ??= main;
217
- options.miniflare = mergeWorkerOptions(
218
- workerOptions,
219
- options.miniflare
220
- );
221
- options.defines = define;
222
- }
223
- return options;
224
- }
225
- async function parseProjectOptions(project) {
226
- const environment = project.config.environment;
227
- if (environment !== void 0 && environment !== "node") {
228
- const quotedEnvironment = JSON.stringify(environment);
229
- let migrationGuide = ".";
230
- if (environment === "miniflare") {
231
- migrationGuide = ", and refer to the migration guide if upgrading from `vitest-environment-miniflare`:\nhttps://developers.cloudflare.com/workers/testing/vitest-integration/get-started/migrate-from-miniflare-2/";
232
- }
233
- const relativePath = getRelativeProjectPath(project);
234
- const message = [
235
- `Unexpected custom \`environment\` ${quotedEnvironment} in project ${relativePath}.`,
236
- "The Workers pool always runs your tests inside of an environment providing Workers runtime APIs.",
237
- `Please remove the \`environment\` configuration${migrationGuide}`,
238
- "Use `poolMatchGlobs`/`environmentMatchGlobs` to run a subset of your tests in a different pool/environment."
239
- ].join("\n");
240
- throw new TypeError(message);
241
- }
242
- const projectPath = getProjectPath(project);
243
- const rootPath = typeof projectPath === "string" ? path2.dirname(projectPath) : "";
244
- const poolOptions = project.config.poolOptions;
245
- let workersPoolOptions = poolOptions?.workers ?? {};
246
- try {
247
- if (typeof workersPoolOptions === "function") {
248
- const inject = (key) => {
249
- return project.getProvidedContext()[key];
250
- };
251
- workersPoolOptions = await workersPoolOptions({ inject });
252
- }
253
- return await parseCustomPoolOptions(rootPath, workersPoolOptions, {
254
- path: OPTIONS_PATH_ARRAY
255
- });
256
- } catch (e) {
257
- if (!isZodErrorLike(e))
258
- throw e;
259
- let formatted;
260
- try {
261
- formatted = formatZodError(e, {
262
- test: { poolOptions: { workers: workersPoolOptions } }
263
- });
264
- } catch (error) {
265
- throw e;
266
- }
267
- const relativePath = getRelativeProjectPath(project);
268
- throw new TypeError(
269
- `Unexpected pool options in project ${relativePath}:
270
- ${formatted}`
271
- );
272
- }
273
- }
274
-
275
- // src/pool/loopback.ts
276
- import assert2 from "node:assert";
277
- import fs from "node:fs/promises";
278
- import path3 from "node:path";
279
- import {
280
- CACHE_PLUGIN_NAME,
281
- D1_PLUGIN_NAME,
282
- DURABLE_OBJECTS_PLUGIN_NAME,
283
- KV_PLUGIN_NAME,
284
- Mutex,
285
- R2_PLUGIN_NAME,
286
- Response
287
- } from "miniflare";
288
- async function handleSnapshotRequest(request, url) {
289
- const filePath = url.searchParams.get("path");
290
- if (filePath === null)
291
- return new Response(null, { status: 400 });
292
- if (request.method === "POST") {
293
- await fs.mkdir(filePath, { recursive: true });
294
- return new Response(null, { status: 204 });
295
- }
296
- if (request.method === "PUT") {
297
- const snapshot = await request.arrayBuffer();
298
- await fs.mkdir(path3.posix.dirname(filePath), { recursive: true });
299
- await fs.writeFile(filePath, new Uint8Array(snapshot));
300
- return new Response(null, { status: 204 });
301
- }
302
- if (request.method === "GET") {
303
- try {
304
- return new Response(await fs.readFile(filePath));
305
- } catch (e) {
306
- if (!isFileNotFoundError(e))
307
- throw e;
308
- return new Response(null, { status: 404 });
309
- }
310
- }
311
- if (request.method === "DELETE") {
312
- try {
313
- await fs.unlink(filePath);
314
- } catch (e) {
315
- if (!isFileNotFoundError(e))
316
- throw e;
317
- }
318
- return new Response(null, { status: 204 });
319
- }
320
- return new Response(null, { status: 405 });
321
- }
322
- async function emptyDir(dirPath) {
323
- let names;
324
- try {
325
- names = await fs.readdir(dirPath);
326
- } catch (e) {
327
- if (isFileNotFoundError(e))
328
- return;
329
- throw e;
330
- }
331
- for (const name of names) {
332
- await fs.rm(path3.join(dirPath, name), { recursive: true, force: true });
333
- }
334
- }
335
- var stackStates = /* @__PURE__ */ new WeakMap();
336
- function getState(mf) {
337
- let state = stackStates.get(mf);
338
- if (state === void 0) {
339
- const persistPaths = mf.unsafeGetPersistPaths();
340
- const durableObjectPersistPath = persistPaths.get("do");
341
- assert2(
342
- durableObjectPersistPath !== void 0,
343
- "Expected Durable Object persist path"
344
- );
345
- state = {
346
- mutex: new Mutex(),
347
- depth: 0,
348
- broken: false,
349
- persistPaths: Array.from(new Set(persistPaths.values())),
350
- durableObjectPersistPath
351
- };
352
- stackStates.set(mf, state);
353
- }
354
- return state;
355
- }
356
- var ABORT_ALL_WORKER_NAME = `${WORKER_NAME_PREFIX}abort-all`;
357
- var ABORT_ALL_WORKER = {
358
- name: ABORT_ALL_WORKER_NAME,
359
- compatibilityFlags: ["unsafe_module"],
360
- modules: [
361
- {
362
- type: "ESModule",
363
- path: "index.mjs",
364
- contents: `
365
- import workerdUnsafe from "workerd:unsafe";
366
- export default {
367
- async fetch(request) {
368
- if (request.method !== "DELETE") return new Response(null, { status: 405 });
369
- await workerdUnsafe.abortAllDurableObjects();
370
- return new Response(null, { status: 204 });
371
- }
372
- };
373
- `
374
- }
375
- ]
376
- };
377
- function scheduleStorageReset(mf) {
378
- const state = getState(mf);
379
- assert2(state.storageResetPromise === void 0);
380
- state.storageResetPromise = state.mutex.runWith(async () => {
381
- const abortAllWorker = await mf.getWorker(ABORT_ALL_WORKER_NAME);
382
- await abortAllWorker.fetch("http://placeholder", { method: "DELETE" });
383
- for (const persistPath of state.persistPaths) {
384
- await emptyDir(persistPath);
385
- }
386
- state.depth = 0;
387
- state.storageResetPromise = void 0;
388
- });
389
- }
390
- async function waitForStorageReset(mf) {
391
- await getState(mf).storageResetPromise;
392
- }
393
- var BLOBS_DIR_NAME = "blobs";
394
- var STACK_DIR_NAME = "__vitest_pool_workers_stack";
395
- async function pushStackedStorage(intoDepth, persistPath) {
396
- const stackFramePath = path3.join(
397
- persistPath,
398
- STACK_DIR_NAME,
399
- intoDepth.toString()
400
- );
401
- await fs.mkdir(stackFramePath, { recursive: true });
402
- for (const key of await fs.readdir(persistPath, { withFileTypes: true })) {
403
- if (key.name === STACK_DIR_NAME)
404
- continue;
405
- const keyPath = path3.join(persistPath, key.name);
406
- const stackFrameKeyPath = path3.join(stackFramePath, key.name);
407
- assert2(key.isDirectory(), `Expected ${keyPath} to be a directory`);
408
- let createdStackFrameKeyPath = false;
409
- for (const name of await fs.readdir(keyPath)) {
410
- if (name === BLOBS_DIR_NAME)
411
- break;
412
- if (!createdStackFrameKeyPath) {
413
- createdStackFrameKeyPath = true;
414
- await fs.mkdir(stackFrameKeyPath);
415
- }
416
- const namePath = path3.join(keyPath, name);
417
- const stackFrameNamePath = path3.join(stackFrameKeyPath, name);
418
- assert2(name.endsWith(".sqlite"), `Expected .sqlite, got ${namePath}`);
419
- await fs.copyFile(namePath, stackFrameNamePath);
420
- }
421
- }
422
- }
423
- async function popStackedStorage(fromDepth, persistPath) {
424
- for (const key of await fs.readdir(persistPath, { withFileTypes: true })) {
425
- if (key.name === STACK_DIR_NAME)
426
- continue;
427
- const keyPath = path3.join(persistPath, key.name);
428
- for (const name of await fs.readdir(keyPath)) {
429
- if (name === BLOBS_DIR_NAME)
430
- break;
431
- const namePath = path3.join(keyPath, name);
432
- assert2(name.endsWith(".sqlite"), `Expected .sqlite, got ${namePath}`);
433
- await fs.unlink(namePath);
434
- }
435
- }
436
- const stackFramePath = path3.join(
437
- persistPath,
438
- STACK_DIR_NAME,
439
- fromDepth.toString()
440
- );
441
- await fs.cp(stackFramePath, persistPath, { recursive: true });
442
- await fs.rm(stackFramePath, { recursive: true, force: true });
443
- }
444
- var PLUGIN_PRODUCT_NAMES = {
445
- [CACHE_PLUGIN_NAME]: "Cache",
446
- [D1_PLUGIN_NAME]: "D1",
447
- [DURABLE_OBJECTS_PLUGIN_NAME]: "Durable Objects",
448
- [KV_PLUGIN_NAME]: "KV",
449
- [R2_PLUGIN_NAME]: "R2"
450
- };
451
- var LIST_FORMAT = new Intl.ListFormat("en-US");
452
- function checkAllStorageOperationsResolved(action, source, persistPaths, results) {
453
- const failedProducts = [];
454
- const lines = [];
455
- for (let i = 0; i < results.length; i++) {
456
- const result = results[i];
457
- if (result.status === "rejected") {
458
- const pluginName = path3.basename(persistPaths[i]);
459
- const productName = PLUGIN_PRODUCT_NAMES[pluginName] ?? pluginName;
460
- failedProducts.push(productName);
461
- lines.push(`- ${result.reason}`);
462
- }
463
- }
464
- if (failedProducts.length > 0) {
465
- const separator = "=".repeat(80);
466
- lines.unshift(
467
- "",
468
- separator,
469
- `Failed to ${action} isolated storage stack frame in ${source}.`,
470
- "This usually means your Worker tried to access storage outside of a test.",
471
- `In particular, we were unable to ${action} ${LIST_FORMAT.format(
472
- failedProducts
473
- )} storage.`,
474
- "Ensure you `await` all `Promise`s that read or write to these services.",
475
- "\x1B[2m"
476
- );
477
- lines.push("\x1B[22m" + separator, "");
478
- console.error(lines.join("\n"));
479
- return false;
480
- }
481
- return true;
482
- }
483
- async function handleStorageRequest(request, mf) {
484
- const state = getState(mf);
485
- if (state.broken) {
486
- return new Response(
487
- "Isolated storage failed. There should be additional logs above.",
488
- { status: 500 }
489
- );
490
- }
491
- const source = request.headers.get("MF-Vitest-Source") ?? "an unknown location";
492
- let success;
493
- if (request.method === "POST") {
494
- success = await state.mutex.runWith(async () => {
495
- state.depth++;
496
- const results = await Promise.allSettled(
497
- state.persistPaths.map(
498
- (persistPath) => pushStackedStorage(state.depth, persistPath)
499
- )
500
- );
501
- return checkAllStorageOperationsResolved(
502
- "push",
503
- source,
504
- state.persistPaths,
505
- results
506
- );
507
- });
508
- } else if (request.method === "DELETE") {
509
- success = await state.mutex.runWith(async () => {
510
- assert2(state.depth > 0, "Stack underflow");
511
- const results = await Promise.allSettled(
512
- state.persistPaths.map(
513
- (persistPath) => popStackedStorage(state.depth, persistPath)
514
- )
515
- );
516
- state.depth--;
517
- return checkAllStorageOperationsResolved(
518
- "pop",
519
- source,
520
- state.persistPaths,
521
- results
522
- );
523
- });
524
- } else {
525
- return new Response(null, { status: 405 });
526
- }
527
- if (success) {
528
- return new Response(null, { status: 204 });
529
- } else {
530
- state.broken = true;
531
- return new Response(
532
- "Isolated storage failed. There should be additional logs above.",
533
- { status: 500 }
534
- );
535
- }
536
- }
537
- async function handleDurableObjectsRequest(request, mf, url) {
538
- if (request.method !== "GET")
539
- return new Response(null, { status: 405 });
540
- const { durableObjectPersistPath } = getState(mf);
541
- const uniqueKey = url.searchParams.get("unique_key");
542
- if (uniqueKey === null)
543
- return new Response(null, { status: 400 });
544
- const namespacePath = path3.join(durableObjectPersistPath, uniqueKey);
545
- const ids = [];
546
- try {
547
- const names = await fs.readdir(namespacePath);
548
- for (const name of names) {
549
- if (name.endsWith(".sqlite")) {
550
- ids.push(name.substring(
551
- 0,
552
- name.length - 7
553
- /* ".sqlite".length */
554
- ));
555
- }
556
- }
557
- } catch (e) {
558
- if (!isFileNotFoundError(e))
559
- throw e;
560
- }
561
- return Response.json(ids);
562
- }
563
- function handleLoopbackRequest(request, mf) {
564
- const url = new URL(request.url);
565
- if (url.pathname === "/snapshot")
566
- return handleSnapshotRequest(request, url);
567
- if (url.pathname === "/storage")
568
- return handleStorageRequest(request, mf);
569
- if (url.pathname === "/durable-objects") {
570
- return handleDurableObjectsRequest(request, mf, url);
571
- }
572
- return new Response(null, { status: 404 });
573
- }
574
-
575
- // src/pool/module-fallback.ts
576
- import assert3 from "node:assert";
577
- import fs2 from "node:fs";
578
- import { createRequire } from "node:module";
579
- import platformPath from "node:path";
580
- import posixPath from "node:path/posix";
581
- import { fileURLToPath, pathToFileURL } from "node:url";
582
- import util from "node:util";
583
- import * as cjsModuleLexer from "cjs-module-lexer";
584
- import { buildSync } from "esbuild";
585
- import { ModuleRuleTypeSchema, Response as Response2 } from "miniflare";
586
- var debuglog = util.debuglog(
587
- "vitest-pool-workers:module-fallback",
588
- (log2) => debuglog = log2
589
- );
590
- var isWindows = process.platform === "win32";
591
- function ensurePosixLikePath(filePath) {
592
- return isWindows ? filePath.replaceAll("\\", "/") : filePath;
593
- }
594
- var __filename = fileURLToPath(import.meta.url);
595
- var __dirname = platformPath.dirname(__filename);
596
- var require2 = createRequire(__filename);
597
- var distPath = ensurePosixLikePath(platformPath.resolve(__dirname, ".."));
598
- var libPath = posixPath.join(distPath, "worker", "lib");
599
- var emptyLibPath = posixPath.join(libPath, "cloudflare/empty-internal.cjs");
600
- var disableCjsEsmShimSuffix = "?mf_vitest_no_cjs_esm_shim";
601
- function trimSuffix(suffix, value) {
602
- assert3(value.endsWith(suffix));
603
- return value.substring(0, value.length - suffix.length);
604
- }
605
- var forceModuleTypeRegexp = new RegExp(
606
- `\\?mf_vitest_force=(${ModuleRuleTypeSchema.options.join("|")})$`
607
- );
608
- var workerdBuiltinModules = /* @__PURE__ */ new Set([
609
- ...define_VITEST_POOL_WORKERS_DEFINE_BUILTIN_MODULES_default,
610
- "__STATIC_CONTENT_MANIFEST"
611
- ]);
612
- var bundleDependencies = ["chai"];
613
- function isFile(filePath) {
614
- try {
615
- return fs2.statSync(filePath).isFile();
616
- } catch (e) {
617
- if (isFileNotFoundError(e))
618
- return false;
619
- throw e;
620
- }
621
- }
622
- function getParentPaths(filePath) {
623
- const parentPaths = [];
624
- while (true) {
625
- const parentPath = posixPath.dirname(filePath);
626
- if (parentPath === filePath)
627
- return parentPaths;
628
- parentPaths.push(parentPath);
629
- filePath = parentPath;
630
- }
631
- }
632
- var dirPathTypeModuleCache = /* @__PURE__ */ new Map();
633
- function isWithinTypeModuleContext(filePath) {
634
- const parentPaths = getParentPaths(filePath);
635
- for (const parentPath of parentPaths) {
636
- const cache = dirPathTypeModuleCache.get(parentPath);
637
- if (cache !== void 0)
638
- return cache;
639
- }
640
- for (const parentPath of parentPaths) {
641
- try {
642
- const pkgPath = posixPath.join(parentPath, "package.json");
643
- const pkgJson = fs2.readFileSync(pkgPath, "utf8");
644
- const pkg = JSON.parse(pkgJson);
645
- const maybeModulePath = pkg.module ? posixPath.join(parentPath, pkg.module) : "";
646
- const cache = pkg.type === "module" || maybeModulePath === filePath;
647
- dirPathTypeModuleCache.set(parentPath, cache);
648
- return cache;
649
- } catch (e) {
650
- if (!isFileNotFoundError(e))
651
- throw e;
652
- }
653
- }
654
- return false;
655
- }
656
- await cjsModuleLexer.init();
657
- async function getCjsNamedExports(vite, filePath, contents, seen = /* @__PURE__ */ new Set()) {
658
- const { exports, reexports } = cjsModuleLexer.parse(contents);
659
- const result = new Set(exports);
660
- for (const reexport of reexports) {
661
- const resolved = await viteResolve(
662
- vite,
663
- reexport,
664
- filePath,
665
- /* isRequire */
666
- true
667
- );
668
- if (seen.has(resolved))
669
- continue;
670
- try {
671
- const resolvedContents = fs2.readFileSync(resolved, "utf8");
672
- seen.add(filePath);
673
- const resolvedNames = await getCjsNamedExports(
674
- vite,
675
- resolved,
676
- resolvedContents,
677
- seen
678
- );
679
- seen.delete(filePath);
680
- for (const name of resolvedNames)
681
- result.add(name);
682
- } catch (e) {
683
- if (!isFileNotFoundError(e))
684
- throw e;
685
- }
686
- }
687
- result.delete("default");
688
- result.delete("__esModule");
689
- return result;
690
- }
691
- function withSourceUrl(contents, url) {
692
- if (contents.lastIndexOf("//# sourceURL=") !== -1)
693
- return contents;
694
- const sourceURL = `
695
- //# sourceURL=${url.toString()}
696
- `;
697
- return contents + sourceURL;
698
- }
699
- function withImportMetaUrl(contents, url) {
700
- return contents.replaceAll("import.meta.url", JSON.stringify(url.toString()));
701
- }
702
- var bundleCache = /* @__PURE__ */ new Map();
703
- function bundleDependency(entryPath) {
704
- let output = bundleCache.get(entryPath);
705
- if (output !== void 0)
706
- return output;
707
- debuglog(`Bundling ${entryPath}...`);
708
- const result = buildSync({
709
- platform: "node",
710
- target: "esnext",
711
- format: "cjs",
712
- bundle: true,
713
- packages: "external",
714
- sourcemap: "inline",
715
- sourcesContent: false,
716
- entryPoints: [entryPath],
717
- write: false
718
- });
719
- assert3(result.outputFiles.length === 1);
720
- output = result.outputFiles[0].text;
721
- bundleCache.set(entryPath, output);
722
- return output;
723
- }
724
- var jsExtensions = [".js", ".mjs", ".cjs"];
725
- function maybeGetTargetFilePath(target) {
726
- if (isFile(target))
727
- return target;
728
- for (const extension of jsExtensions) {
729
- const targetWithExtension = target + extension;
730
- if (fs2.existsSync(targetWithExtension))
731
- return targetWithExtension;
732
- }
733
- if (target.endsWith(disableCjsEsmShimSuffix))
734
- return target;
735
- }
736
- function getApproximateSpecifier(target, referrerDir) {
737
- if (/^(node|cloudflare|workerd):/.test(target))
738
- return target;
739
- return posixPath.relative(referrerDir, target);
740
- }
741
- async function viteResolve(vite, specifier, referrer, isRequire) {
742
- const resolved = await vite.pluginContainer.resolveId(specifier, referrer, {
743
- ssr: true,
744
- // https://github.com/vitejs/vite/blob/v5.1.4/packages/vite/src/node/plugins/resolve.ts#L178-L179
745
- custom: { "node-resolve": { isRequire } }
746
- });
747
- if (resolved === null) {
748
- if (isRequire && specifier[0] === ".") {
749
- return require2.resolve(specifier, { paths: [referrer] });
750
- }
751
- throw new Error("Not found");
752
- }
753
- if (resolved.id === "__vite-browser-external")
754
- return emptyLibPath;
755
- if (resolved.external) {
756
- let { id } = resolved;
757
- if (workerdBuiltinModules.has(id))
758
- return `/${id}`;
759
- id = `node:${id}`;
760
- if (workerdBuiltinModules.has(id))
761
- return `/${id}`;
762
- throw new Error("Not found");
763
- }
764
- return resolved.id;
765
- }
766
- async function resolve(vite, method, target, specifier, referrer) {
767
- const referrerDir = posixPath.dirname(referrer);
768
- let filePath = maybeGetTargetFilePath(target);
769
- if (filePath !== void 0)
770
- return filePath;
771
- if (referrerDir !== "/" && workerdBuiltinModules.has(specifier)) {
772
- return `/${specifier}`;
773
- }
774
- const specifierLibPath = posixPath.join(
775
- libPath,
776
- specifier.replaceAll(":", "/")
777
- );
778
- filePath = maybeGetTargetFilePath(specifierLibPath);
779
- if (filePath !== void 0)
780
- return filePath;
781
- return viteResolve(vite, specifier, referrer, method === "require");
782
- }
783
- function buildRedirectResponse(filePath) {
784
- if (isWindows && filePath[0] !== "/")
785
- filePath = `/${filePath}`;
786
- return new Response2(null, { status: 301, headers: { Location: filePath } });
787
- }
788
- function maybeGetForceTypeModuleContents(filePath) {
789
- const match = forceModuleTypeRegexp.exec(filePath);
790
- if (match === null)
791
- return;
792
- filePath = trimSuffix(match[0], filePath);
793
- const type = match[1];
794
- const contents = fs2.readFileSync(filePath);
795
- switch (type) {
796
- case "ESModule":
797
- return { esModule: contents.toString() };
798
- case "CommonJS":
799
- return { commonJsModule: contents.toString() };
800
- case "NodeJsCompatModule":
801
- return { nodeJsCompatModule: contents.toString() };
802
- case "Text":
803
- return { text: contents.toString() };
804
- case "Data":
805
- return { data: contents };
806
- case "CompiledWasm":
807
- return { wasm: contents };
808
- case "PythonModule":
809
- return { pythonModule: contents.toString() };
810
- case "PythonRequirement":
811
- return { pythonRequirement: contents.toString() };
812
- default: {
813
- const exhaustive = type;
814
- assert3.fail(`Unreachable: ${exhaustive} modules are unsupported`);
815
- }
816
- }
817
- }
818
- function buildModuleResponse(target, contents) {
819
- let name = target;
820
- if (!isWindows)
821
- name = posixPath.relative("/", target);
822
- assert3(name[0] !== "/");
823
- const result = { name };
824
- for (const key in contents) {
825
- const value = contents[key];
826
- result[key] = value instanceof Uint8Array ? Array.from(value) : value;
827
- }
828
- return Response2.json(result);
829
- }
830
- async function load(vite, logBase, method, target, specifier, filePath) {
831
- if (target !== filePath) {
832
- if (method === "require" && !specifier.startsWith("node:")) {
833
- filePath += disableCjsEsmShimSuffix;
834
- }
835
- debuglog(logBase, "redirect:", filePath);
836
- return buildRedirectResponse(filePath);
837
- }
838
- if (filePath.endsWith(".wasm"))
839
- filePath += `?mf_vitest_force=CompiledWasm`;
840
- const maybeContents = maybeGetForceTypeModuleContents(filePath);
841
- if (maybeContents !== void 0) {
842
- debuglog(logBase, "forced:", filePath);
843
- return buildModuleResponse(target, maybeContents);
844
- }
845
- const disableCjsEsmShim = filePath.endsWith(disableCjsEsmShimSuffix);
846
- if (disableCjsEsmShim) {
847
- filePath = trimSuffix(disableCjsEsmShimSuffix, filePath);
848
- }
849
- let isEsm = filePath.endsWith(".mjs") || filePath.endsWith(".js") && isWithinTypeModuleContext(filePath);
850
- let contents;
851
- const maybeBundled = bundleCache.get(filePath);
852
- if (maybeBundled !== void 0) {
853
- contents = maybeBundled;
854
- isEsm = false;
855
- } else {
856
- contents = fs2.readFileSync(filePath, "utf8");
857
- }
858
- const targetUrl = pathToFileURL(target);
859
- contents = withSourceUrl(contents, targetUrl);
860
- if (isEsm) {
861
- contents = withImportMetaUrl(contents, targetUrl);
862
- debuglog(logBase, "esm:", filePath);
863
- return buildModuleResponse(target, { esModule: contents });
864
- }
865
- const insertCjsEsmShim = method === "import" || specifier.startsWith("node:");
866
- if (insertCjsEsmShim && !disableCjsEsmShim) {
867
- const fileName = posixPath.basename(filePath);
868
- const disableShimSpecifier = `./${fileName}${disableCjsEsmShimSuffix}`;
869
- const quotedDisableShimSpecifier = JSON.stringify(disableShimSpecifier);
870
- let esModule = `import mod from ${quotedDisableShimSpecifier}; export default mod;`;
871
- for (const name of await getCjsNamedExports(vite, filePath, contents)) {
872
- esModule += ` export const ${name} = mod.${name};`;
873
- }
874
- debuglog(logBase, "cjs-esm-shim:", filePath);
875
- return buildModuleResponse(target, { esModule });
876
- }
877
- debuglog(logBase, "cjs:", filePath);
878
- return buildModuleResponse(target, { nodeJsCompatModule: contents });
879
- }
880
- async function handleModuleFallbackRequest(vite, request) {
881
- const method = request.headers.get("X-Resolve-Method");
882
- assert3(method === "import" || method === "require");
883
- const url = new URL(request.url);
884
- let target = url.searchParams.get("specifier");
885
- let referrer = url.searchParams.get("referrer");
886
- assert3(target !== null, "Expected specifier search param");
887
- assert3(referrer !== null, "Expected referrer search param");
888
- const referrerDir = posixPath.dirname(referrer);
889
- let specifier = getApproximateSpecifier(target, referrerDir);
890
- if (specifier.startsWith("file:"))
891
- specifier = specifier.substring(5);
892
- if (isWindows) {
893
- if (target[0] === "/")
894
- target = target.substring(1);
895
- if (referrer[0] === "/")
896
- referrer = referrer.substring(1);
897
- }
898
- const quotedTarget = JSON.stringify(target);
899
- const logBase = `${method}(${quotedTarget}) relative to ${referrer}:`;
900
- try {
901
- const filePath = await resolve(vite, method, target, specifier, referrer);
902
- if (bundleDependencies.includes(specifier))
903
- bundleDependency(filePath);
904
- return await load(vite, logBase, method, target, specifier, filePath);
905
- } catch (e) {
906
- debuglog(logBase, "error:", e);
907
- }
908
- return new Response2(null, { status: 404 });
909
- }
910
-
911
- // src/pool/index.ts
912
- assert4(
913
- typeof __vite_ssr_import__ === "undefined",
914
- "Expected `@cloudflare/vitest-pool-workers` not to be transformed by Vite"
915
- );
916
- function structuredSerializableStringify(value) {
917
- return devalue.stringify(value, structuredSerializableReducers);
918
- }
919
- function structuredSerializableParse(value) {
920
- return devalue.parse(value, structuredSerializableRevivers);
921
- }
922
- var debuglog2 = util2.debuglog(
923
- "vitest-pool-workers:index",
924
- (fn) => debuglog2 = fn
925
- );
926
- var log = new Log(LogLevel.VERBOSE, { prefix: "vpw" });
927
- var mfLog = new Log(LogLevel.WARN);
928
- var __filename2 = fileURLToPath2(import.meta.url);
929
- var __dirname2 = path4.dirname(__filename2);
930
- var DIST_PATH = path4.resolve(__dirname2, "..");
931
- var POOL_WORKER_PATH = path4.join(DIST_PATH, "worker", "index.mjs");
932
- var symbolizerWarning = "warning: Not symbolizing stack traces because $LLVM_SYMBOLIZER is not set.";
933
- var ignoreMessages = [
934
- // Not user actionable
935
- // TODO(someday): this is normal operation and really shouldn't error
936
- "disconnected: operation canceled",
937
- "disconnected: worker_do_not_log; Request failed due to internal error",
938
- "disconnected: WebSocket was aborted"
939
- ];
940
- function trimSymbolizerWarning(chunk) {
941
- return chunk.includes(symbolizerWarning) ? chunk.substring(chunk.indexOf("\n") + 1) : chunk;
942
- }
943
- function handleRuntimeStdio(stdout, stderr) {
944
- stdout.on("data", (chunk) => {
945
- process.stdout.write(chunk);
946
- });
947
- stderr.on("data", (chunk) => {
948
- const str = trimSymbolizerWarning(chunk.toString());
949
- if (ignoreMessages.some((message) => str.includes(message))) {
950
- return;
951
- }
952
- process.stderr.write(str);
953
- });
954
- }
955
- function forEachMiniflare(mfs, callback) {
956
- if (mfs instanceof Miniflare)
957
- return callback(mfs);
958
- const promises = [];
959
- for (const mf of mfs.values())
960
- promises.push(callback(mf));
961
- return Promise.all(promises);
962
- }
963
- var allProjects = /* @__PURE__ */ new Map();
964
- function getRunnerName(project, testFile) {
965
- const name = `${WORKER_NAME_PREFIX}runner-${project.getName()}`;
966
- if (testFile === void 0)
967
- return name;
968
- const testFileHash = crypto.createHash("sha1").update(testFile).digest("hex");
969
- testFile = testFile.replace(/[^a-z0-9-]/gi, "_");
970
- return `${name}-${testFileHash}-${testFile}`;
971
- }
972
- function isDurableObjectDesignatorToSelf(value) {
973
- if (typeof value === "string")
974
- return true;
975
- return typeof value === "object" && value !== null && "className" in value && typeof value.className === "string" && (!("scriptName" in value) || value.scriptName === void 0);
976
- }
977
- function getDurableObjectDesignators(options) {
978
- const result = /* @__PURE__ */ new Map();
979
- const durableObjects = options.miniflare?.durableObjects ?? {};
980
- for (const [key, designator] of Object.entries(durableObjects)) {
981
- if (typeof designator === "string") {
982
- result.set(key, { className: USER_OBJECT_MODULE_NAME + designator });
983
- } else if (typeof designator.unsafeUniqueKey !== "symbol") {
984
- let className = designator.className;
985
- if (designator.scriptName === void 0) {
986
- className = USER_OBJECT_MODULE_NAME + className;
987
- }
988
- result.set(key, {
989
- className,
990
- scriptName: designator.scriptName,
991
- unsafeUniqueKey: designator.unsafeUniqueKey
992
- });
993
- }
994
- }
995
- return result;
996
- }
997
- var POOL_WORKER_DIR = path4.dirname(POOL_WORKER_PATH);
998
- var USER_OBJECT_MODULE_NAME = "__VITEST_POOL_WORKERS_USER_OBJECT";
999
- var USER_OBJECT_MODULE_PATH = path4.join(
1000
- POOL_WORKER_DIR,
1001
- USER_OBJECT_MODULE_NAME
1002
- );
1003
- var DEFINES_MODULE_PATH = path4.join(
1004
- POOL_WORKER_DIR,
1005
- "__VITEST_POOL_WORKERS_DEFINES"
1006
- );
1007
- function fixupDurableObjectBindingsToSelf(worker) {
1008
- const result = /* @__PURE__ */ new Set();
1009
- if (worker.durableObjects === void 0)
1010
- return result;
1011
- for (const key of Object.keys(worker.durableObjects)) {
1012
- const designator = worker.durableObjects[key];
1013
- if (typeof designator === "string") {
1014
- result.add(designator);
1015
- worker.durableObjects[key] = USER_OBJECT_MODULE_NAME + designator;
1016
- } else if (isDurableObjectDesignatorToSelf(designator)) {
1017
- result.add(designator.className);
1018
- worker.durableObjects[key] = {
1019
- ...designator,
1020
- className: USER_OBJECT_MODULE_NAME + designator.className
1021
- };
1022
- }
1023
- }
1024
- return result;
1025
- }
1026
- var SELF_NAME_BINDING = "__VITEST_POOL_WORKERS_SELF_NAME";
1027
- var SELF_SERVICE_BINDING = "__VITEST_POOL_WORKERS_SELF_SERVICE";
1028
- var LOOPBACK_SERVICE_BINDING = "__VITEST_POOL_WORKERS_LOOPBACK_SERVICE";
1029
- var RUNNER_OBJECT_BINDING = "__VITEST_POOL_WORKERS_RUNNER_OBJECT";
1030
- var numericCompare = new Intl.Collator("en", { numeric: true }).compare;
1031
- function assertCompatibilityFlagEnabled(opts) {
1032
- const hasWranglerConfig = opts.relativeWranglerConfigPath !== void 0;
1033
- if (opts.disableFlag !== void 0 && opts.compatibilityFlags.includes(opts.disableFlag)) {
1034
- let message = `In project ${opts.relativeProjectPath}`;
1035
- if (hasWranglerConfig) {
1036
- message += `'s configuration file ${opts.relativeWranglerConfigPath}, \`compatibility_flags\` must not contain "${opts.disableFlag}".
1037
- Similarly`;
1038
- }
1039
- message += `, \`${OPTIONS_PATH}.miniflare.compatibilityFlags\` must not contain "${opts.disableFlag}".
1040
- This flag is incompatible with \`@cloudflare/vitest-pool-workers\`.`;
1041
- throw new Error(message);
1042
- }
1043
- const enabledByFlag = opts.compatibilityFlags.includes(opts.enableFlag);
1044
- const enabledByDate = opts.compatibilityDate !== void 0 && opts.defaultOnDate !== void 0 && numericCompare(opts.compatibilityDate, opts.defaultOnDate) >= 0;
1045
- if (!(enabledByFlag || enabledByDate)) {
1046
- let message = `In project ${opts.relativeProjectPath}`;
1047
- if (hasWranglerConfig) {
1048
- message += `'s configuration file ${opts.relativeWranglerConfigPath}, \`compatibility_flags\` must contain "${opts.enableFlag}"`;
1049
- } else {
1050
- message += `, \`${OPTIONS_PATH}.miniflare.compatibilityFlags\` must contain "${opts.enableFlag}"`;
1051
- }
1052
- if (opts.defaultOnDate !== void 0) {
1053
- if (hasWranglerConfig) {
1054
- message += `, or \`compatibility_date\` must be >= "${opts.defaultOnDate}"`;
1055
- } else {
1056
- message += `, or \`${OPTIONS_PATH}.miniflare.compatibilityDate\` must be >= "${opts.defaultOnDate}"`;
1057
- }
1058
- }
1059
- message += ".\nThis flag is required to use `@cloudflare/vitest-pool-workers`.";
1060
- throw new Error(message);
1061
- }
1062
- }
1063
- function buildProjectWorkerOptions(project) {
1064
- const relativeWranglerConfigPath = maybeApply(
1065
- (v) => path4.relative("", v),
1066
- project.options.wrangler?.configPath
1067
- );
1068
- const runnerWorker = project.options.miniflare ?? {};
1069
- runnerWorker.name = getRunnerName(project.project);
1070
- runnerWorker.bindings ??= {};
1071
- runnerWorker.bindings[SELF_NAME_BINDING] = runnerWorker.name;
1072
- runnerWorker.compatibilityFlags ??= [];
1073
- assertCompatibilityFlagEnabled({
1074
- compatibilityFlags: runnerWorker.compatibilityFlags,
1075
- compatibilityDate: runnerWorker.compatibilityDate,
1076
- relativeProjectPath: project.relativePath,
1077
- relativeWranglerConfigPath,
1078
- // https://developers.cloudflare.com/workers/configuration/compatibility-dates/#commonjs-modules-do-not-export-a-module-namespace
1079
- enableFlag: "export_commonjs_default",
1080
- disableFlag: "export_commonjs_namespace",
1081
- defaultOnDate: "2022-10-31"
1082
- });
1083
- assertCompatibilityFlagEnabled({
1084
- compatibilityFlags: runnerWorker.compatibilityFlags,
1085
- compatibilityDate: runnerWorker.compatibilityDate,
1086
- relativeProjectPath: project.relativePath,
1087
- relativeWranglerConfigPath,
1088
- enableFlag: "nodejs_compat"
1089
- });
1090
- if (!runnerWorker.compatibilityFlags.includes("unsafe_module")) {
1091
- runnerWorker.compatibilityFlags.push("unsafe_module");
1092
- }
1093
- runnerWorker.unsafeEvalBinding = "__VITEST_POOL_WORKERS_UNSAFE_EVAL";
1094
- runnerWorker.unsafeUseModuleFallbackService = true;
1095
- runnerWorker.serviceBindings ??= {};
1096
- runnerWorker.serviceBindings[SELF_SERVICE_BINDING] = kCurrentWorker;
1097
- runnerWorker.serviceBindings[LOOPBACK_SERVICE_BINDING] = handleLoopbackRequest;
1098
- runnerWorker.durableObjects ??= {};
1099
- const durableObjectClassNames = fixupDurableObjectBindingsToSelf(runnerWorker);
1100
- const durableObjectWrappers = Array.from(durableObjectClassNames).sort().map((className) => {
1101
- const quotedClassName = JSON.stringify(className);
1102
- return `export const ${USER_OBJECT_MODULE_NAME}${className} = createDurableObjectWrapper(${quotedClassName});`;
1103
- });
1104
- durableObjectWrappers.unshift(
1105
- 'import { createDurableObjectWrapper } from "cloudflare:test-internal";'
1106
- );
1107
- runnerWorker.durableObjects[RUNNER_OBJECT_BINDING] = {
1108
- className: "RunnerObject",
1109
- // Make the runner object ephemeral, so it doesn't write any `.sqlite` files
1110
- // that would disrupt stacked storage because we prevent eviction
1111
- unsafeUniqueKey: kUnsafeEphemeralUniqueKey,
1112
- unsafePreventEviction: true
1113
- };
1114
- const defines = `export default {
1115
- ${Object.entries(project.options.defines ?? {}).map(([key, value]) => `${JSON.stringify(key)}: ${value}`).join(",\n")}
1116
- };
1117
- `;
1118
- if ("script" in runnerWorker)
1119
- delete runnerWorker.script;
1120
- if ("scriptPath" in runnerWorker)
1121
- delete runnerWorker.scriptPath;
1122
- const modulesRoot = process.platform === "win32" ? "Z:\\" : "/";
1123
- runnerWorker.modulesRoot = modulesRoot;
1124
- runnerWorker.modules = [
1125
- {
1126
- type: "ESModule",
1127
- path: path4.join(modulesRoot, POOL_WORKER_PATH),
1128
- contents: fs3.readFileSync(POOL_WORKER_PATH)
1129
- },
1130
- {
1131
- type: "ESModule",
1132
- path: path4.join(modulesRoot, USER_OBJECT_MODULE_PATH),
1133
- contents: durableObjectWrappers.join("\n")
1134
- },
1135
- {
1136
- type: "ESModule",
1137
- path: path4.join(modulesRoot, DEFINES_MODULE_PATH),
1138
- contents: defines
1139
- }
1140
- ];
1141
- const workers = [runnerWorker];
1142
- if (runnerWorker.workers !== void 0) {
1143
- for (let i = 0; i < runnerWorker.workers.length; i++) {
1144
- const worker = runnerWorker.workers[i];
1145
- if (typeof worker !== "object" || worker === null || !("name" in worker) || typeof worker.name !== "string" || worker.name === "") {
1146
- throw new Error(
1147
- `In project ${project.relativePath}, \`${OPTIONS_PATH}.miniflare.workers[${i}].name\` must be non-empty`
1148
- );
1149
- }
1150
- if (worker.name.startsWith(WORKER_NAME_PREFIX)) {
1151
- throw new Error(
1152
- `In project ${project.relativePath}, \`${OPTIONS_PATH}.miniflare.workers[${i}].name\` must not start with "${WORKER_NAME_PREFIX}", got ${worker.name}`
1153
- );
1154
- }
1155
- workers.push(worker);
1156
- }
1157
- delete runnerWorker.workers;
1158
- }
1159
- return workers;
1160
- }
1161
- var SHARED_MINIFLARE_OPTIONS = {
1162
- log: mfLog,
1163
- verbose: true,
1164
- handleRuntimeStdio,
1165
- unsafeStickyBlobs: true
1166
- };
1167
- var moduleFallbackServices = /* @__PURE__ */ new WeakMap();
1168
- function getModuleFallbackService(ctx) {
1169
- let service = moduleFallbackServices.get(ctx);
1170
- if (service !== void 0)
1171
- return service;
1172
- service = handleModuleFallbackRequest.bind(void 0, ctx.vitenode.server);
1173
- moduleFallbackServices.set(ctx, service);
1174
- return service;
1175
- }
1176
- function buildProjectMiniflareOptions(ctx, project) {
1177
- const moduleFallbackService = getModuleFallbackService(ctx);
1178
- const [runnerWorker, ...auxiliaryWorkers] = buildProjectWorkerOptions(project);
1179
- assert4(runnerWorker.name !== void 0);
1180
- assert4(runnerWorker.name.startsWith(WORKER_NAME_PREFIX));
1181
- if (project.options.singleWorker || project.options.isolatedStorage) {
1182
- return {
1183
- ...SHARED_MINIFLARE_OPTIONS,
1184
- unsafeModuleFallbackService: moduleFallbackService,
1185
- workers: [runnerWorker, ABORT_ALL_WORKER, ...auxiliaryWorkers]
1186
- };
1187
- } else {
1188
- const testWorkers = [];
1189
- for (const testFile of project.testFiles) {
1190
- const testWorker = { ...runnerWorker };
1191
- testWorker.name = getRunnerName(project.project, testFile);
1192
- assert4(testWorker.bindings !== void 0);
1193
- testWorker.bindings = { ...testWorker.bindings };
1194
- testWorker.bindings[SELF_NAME_BINDING] = testWorker.name;
1195
- testWorkers.push(testWorker);
1196
- }
1197
- return {
1198
- ...SHARED_MINIFLARE_OPTIONS,
1199
- unsafeModuleFallbackService: moduleFallbackService,
1200
- workers: [...testWorkers, ABORT_ALL_WORKER, ...auxiliaryWorkers]
1201
- };
1202
- }
1203
- }
1204
- async function getProjectMiniflare(ctx, project) {
1205
- const mfOptions = buildProjectMiniflareOptions(ctx, project);
1206
- const changed = !util2.isDeepStrictEqual(project.previousMfOptions, mfOptions);
1207
- project.previousMfOptions = mfOptions;
1208
- const previousSingleInstance = project.mf instanceof Miniflare;
1209
- const singleInstance = project.options.singleWorker || !project.options.isolatedStorage;
1210
- if (project.mf !== void 0 && previousSingleInstance !== singleInstance) {
1211
- log.info(`Isolation changed for ${project.relativePath}, resetting...`);
1212
- await forEachMiniflare(project.mf, (mf) => mf.dispose());
1213
- project.mf = void 0;
1214
- }
1215
- if (project.mf === void 0) {
1216
- if (singleInstance) {
1217
- log.info(`Starting single runtime for ${project.relativePath}...`);
1218
- project.mf = new Miniflare(mfOptions);
1219
- } else {
1220
- log.info(`Starting isolated runtimes for ${project.relativePath}...`);
1221
- project.mf = /* @__PURE__ */ new Map();
1222
- for (const testFile of project.testFiles) {
1223
- project.mf.set(testFile, new Miniflare(mfOptions));
1224
- }
1225
- }
1226
- await forEachMiniflare(project.mf, (mf) => mf.ready);
1227
- } else if (changed) {
1228
- log.info(`Options changed for ${project.relativePath}, updating...`);
1229
- await forEachMiniflare(project.mf, (mf) => mf.setOptions(mfOptions));
1230
- } else {
1231
- log.debug(`Reusing runtime for ${project.relativePath}...`);
1232
- }
1233
- return project.mf;
1234
- }
1235
- function maybeGetResolvedMainPath(project) {
1236
- const projectPath = getProjectPath(project.project);
1237
- const main = project.options.main;
1238
- if (main === void 0)
1239
- return;
1240
- if (typeof projectPath === "string") {
1241
- return path4.resolve(path4.dirname(projectPath), main);
1242
- } else {
1243
- return path4.resolve(main);
1244
- }
1245
- }
1246
- async function runTests(ctx, mf, workerName, project, config, files, invalidates = []) {
1247
- let workerPath = path4.join(ctx.distPath, "worker.js");
1248
- let threadsWorkerPath = path4.join(ctx.distPath, "workers", "threads.js");
1249
- if (process.platform === "win32") {
1250
- workerPath = `/${ensurePosixLikePath(workerPath)}`;
1251
- threadsWorkerPath = `/${ensurePosixLikePath(threadsWorkerPath)}`;
1252
- }
1253
- ctx.state.clearFiles(project.project, files);
1254
- const data = {
1255
- pool: "threads",
1256
- worker: threadsWorkerPath,
1257
- port: void 0,
1258
- config,
1259
- files,
1260
- invalidates,
1261
- environment: { name: "node", options: null },
1262
- workerId: 0,
1263
- projectName: project.project.getName(),
1264
- providedContext: project.project.getProvidedContext()
1265
- };
1266
- await waitForStorageReset(mf);
1267
- const ns = await mf.getDurableObjectNamespace(
1268
- RUNNER_OBJECT_BINDING,
1269
- workerName
1270
- );
1271
- const stub = ns.get("singleton");
1272
- const res = await stub.fetch("http://placeholder", {
1273
- headers: {
1274
- Upgrade: "websocket",
1275
- "MF-Vitest-Worker-Data": structuredSerializableStringify({
1276
- filePath: workerPath,
1277
- name: "run",
1278
- data
1279
- })
1280
- }
1281
- });
1282
- const webSocket = res.webSocket;
1283
- assert4(webSocket !== null);
1284
- const chunkingSocket = createChunkingSocket({
1285
- post(message) {
1286
- webSocket.send(message);
1287
- },
1288
- on(listener) {
1289
- webSocket.addEventListener("message", (event2) => {
1290
- listener(event2.data);
1291
- });
1292
- }
1293
- });
1294
- const rules = project.options.miniflare?.modulesRules;
1295
- const compiledRules = compileModuleRules(rules ?? []);
1296
- const localRpcFunctions = createMethodsRPC(project.project);
1297
- const patchedLocalRpcFunctions = {
1298
- ...localRpcFunctions,
1299
- async fetch(...args) {
1300
- const specifier = args[0];
1301
- if (/^(cloudflare|workerd):/.test(specifier) || workerdBuiltinModules.has(specifier)) {
1302
- return { externalize: specifier };
1303
- }
1304
- const maybeRule = compiledRules.find(
1305
- (rule) => testRegExps(rule.include, specifier)
1306
- );
1307
- if (maybeRule !== void 0) {
1308
- const externalize = specifier + `?mf_vitest_force=${maybeRule.type}`;
1309
- return { externalize };
1310
- }
1311
- return localRpcFunctions.fetch(...args);
1312
- }
1313
- };
1314
- let startupError;
1315
- const rpc = createBirpc(patchedLocalRpcFunctions, {
1316
- eventNames: ["onCancel"],
1317
- post(value) {
1318
- if (webSocket.readyState === WebSocket.READY_STATE_OPEN) {
1319
- debuglog2("POOL-->WORKER", value);
1320
- chunkingSocket.post(structuredSerializableStringify(value));
1321
- } else {
1322
- debuglog2("POOL--* ", value);
1323
- }
1324
- },
1325
- on(listener) {
1326
- chunkingSocket.on((message) => {
1327
- const value = structuredSerializableParse(message);
1328
- debuglog2("POOL<--WORKER", value);
1329
- if (typeof value === "object" && value !== null && "vitestPoolWorkersError" in value) {
1330
- startupError = value.vitestPoolWorkersError;
1331
- } else {
1332
- listener(value);
1333
- }
1334
- });
1335
- }
1336
- });
1337
- project.project.ctx.onCancel((reason) => rpc.onCancel(reason));
1338
- webSocket.accept();
1339
- const [event] = await events.once(webSocket, "close");
1340
- if (webSocket.readyState === WebSocket.READY_STATE_CLOSING) {
1341
- if (event.code === 1005) {
1342
- webSocket.close();
1343
- } else {
1344
- webSocket.close(event.code, event.reason);
1345
- }
1346
- }
1347
- if (event.code !== 1e3) {
1348
- throw startupError ?? new Error("Failed to run tests");
1349
- }
1350
- debuglog2("DONE", files);
1351
- }
1352
- function getPackageJson(dirPath) {
1353
- while (true) {
1354
- const pkgJsonPath = path4.join(dirPath, "package.json");
1355
- try {
1356
- const contents = fs3.readFileSync(pkgJsonPath, "utf8");
1357
- return JSON.parse(contents);
1358
- } catch (e) {
1359
- if (!isFileNotFoundError(e))
1360
- throw e;
1361
- }
1362
- const nextDirPath = path4.dirname(dirPath);
1363
- if (nextDirPath === dirPath)
1364
- return;
1365
- dirPath = nextDirPath;
1366
- }
1367
- }
1368
- function assertCompatibleVitestVersion(ctx) {
1369
- const poolPkgJson = getPackageJson(__dirname2);
1370
- const vitestPkgJson = getPackageJson(ctx.distPath);
1371
- assert4(
1372
- poolPkgJson !== void 0,
1373
- "Expected to find `package.json` for `@cloudflare/vitest-pool-workers`"
1374
- );
1375
- assert4(
1376
- vitestPkgJson !== void 0,
1377
- "Expected to find `package.json` for `vitest`"
1378
- );
1379
- const expectedVitestVersion = poolPkgJson.peerDependencies?.vitest;
1380
- const actualVitestVersion = vitestPkgJson.version;
1381
- assert4(
1382
- expectedVitestVersion !== void 0,
1383
- "Expected to find `@cloudflare/vitest-pool-workers`'s `vitest` version constraint"
1384
- );
1385
- assert4(
1386
- actualVitestVersion !== void 0,
1387
- "Expected to find `vitest`'s version"
1388
- );
1389
- if (expectedVitestVersion !== actualVitestVersion) {
1390
- const message = [
1391
- `You're running \`vitest@${actualVitestVersion}\`, but this version of \`@cloudflare/vitest-pool-workers\` only supports \`vitest@${expectedVitestVersion}\`.`,
1392
- "`@cloudflare/vitest-pool-workers` currently depends on internal Vitest APIs that are not protected by semantic-versioning guarantees.",
1393
- `Please install \`vitest@${expectedVitestVersion}\` to continue using \`@cloudflare/vitest-pool-workers\`.`
1394
- ].join("\n");
1395
- throw new Error(message);
1396
- }
1397
- }
1398
- function pool_default(ctx) {
1399
- assertCompatibleVitestVersion(ctx);
1400
- return {
1401
- name: "vitest-pool-workers",
1402
- async runTests(specs, invalidates) {
1403
- const parsedProjectOptions = /* @__PURE__ */ new Set();
1404
- for (const [project, testFile] of specs) {
1405
- const projectName = project.getName();
1406
- let workersProject = allProjects.get(projectName);
1407
- if (workersProject === void 0) {
1408
- workersProject = {
1409
- project,
1410
- options: await parseProjectOptions(project),
1411
- testFiles: /* @__PURE__ */ new Set(),
1412
- relativePath: getRelativeProjectPath(project)
1413
- };
1414
- allProjects.set(projectName, workersProject);
1415
- } else if (!parsedProjectOptions.has(project)) {
1416
- workersProject.project = project;
1417
- workersProject.options = await parseProjectOptions(project);
1418
- workersProject.relativePath = getRelativeProjectPath(project);
1419
- }
1420
- workersProject.testFiles.add(testFile);
1421
- parsedProjectOptions.add(project);
1422
- }
1423
- const resultPromises = [];
1424
- const filesByProject = /* @__PURE__ */ new Map();
1425
- for (const [project, file] of specs) {
1426
- let group = filesByProject.get(project);
1427
- if (group === void 0)
1428
- filesByProject.set(project, group = []);
1429
- group.push(file);
1430
- }
1431
- for (const [workspaceProject, files] of filesByProject) {
1432
- const project = allProjects.get(workspaceProject.getName());
1433
- assert4(project !== void 0);
1434
- const options = project.options;
1435
- const config = workspaceProject.getSerializableConfig();
1436
- config.runner = "cloudflare:test-runner";
1437
- config.fakeTimers.toFake = config.fakeTimers.toFake?.filter(
1438
- (method) => method !== "setImmediate" && method !== "clearImmediate"
1439
- );
1440
- config.poolOptions = {
1441
- threads: {
1442
- // Allow workers to be re-used by removing the isolation requirement
1443
- isolate: false
1444
- },
1445
- workers: {
1446
- // Include resolved `main` if defined, and the names of Durable Object
1447
- // bindings that point to classes in the current isolate in the
1448
- // serialized config
1449
- main: maybeGetResolvedMainPath(project),
1450
- // Include designators of all Durable Object namespaces bound in the
1451
- // runner worker. We'll use this to list IDs in a namespace. We'll
1452
- // also use this to check Durable Object test runner helpers are
1453
- // only used with classes defined in the current worker, as these
1454
- // helpers rely on wrapping the object.
1455
- durableObjectBindingDesignators: getDurableObjectDesignators(
1456
- project.options
1457
- ),
1458
- // Include whether isolated storage has been enabled for this
1459
- // project, so we know whether to call out to the loopback service
1460
- // to push/pop the storage stack between tests.
1461
- isolatedStorage: project.options.isolatedStorage
1462
- }
1463
- };
1464
- const mf = await getProjectMiniflare(ctx, project);
1465
- if (options.singleWorker) {
1466
- assert4(mf instanceof Miniflare, "Expected single instance");
1467
- const name = getRunnerName(workspaceProject);
1468
- resultPromises.push(
1469
- runTests(ctx, mf, name, project, config, files, invalidates)
1470
- );
1471
- } else if (options.isolatedStorage) {
1472
- assert4(mf instanceof Map, "Expected multiple isolated instances");
1473
- const name = getRunnerName(workspaceProject);
1474
- for (const file of files) {
1475
- const fileMf = mf.get(file);
1476
- assert4(fileMf !== void 0);
1477
- resultPromises.push(
1478
- runTests(ctx, fileMf, name, project, config, [file], invalidates)
1479
- );
1480
- }
1481
- } else {
1482
- assert4(mf instanceof Miniflare, "Expected single instance");
1483
- for (const file of files) {
1484
- const name = getRunnerName(workspaceProject, file);
1485
- resultPromises.push(
1486
- runTests(ctx, mf, name, project, config, [file], invalidates)
1487
- );
1488
- }
1489
- }
1490
- }
1491
- const results = await Promise.allSettled(resultPromises);
1492
- const errors = results.filter((r) => r.status === "rejected").map((r) => r.reason);
1493
- for (const project of allProjects.values()) {
1494
- if (project.mf !== void 0) {
1495
- void forEachMiniflare(
1496
- project.mf,
1497
- async (mf) => scheduleStorageReset(mf)
1498
- );
1499
- }
1500
- }
1501
- if (errors.length > 0) {
1502
- throw new AggregateError(
1503
- errors,
1504
- "Errors occurred while running tests. For more information, see serialized error."
1505
- );
1506
- }
1507
- },
1508
- async close() {
1509
- log.debug("Shutting down runtimes...");
1510
- const promises = [];
1511
- for (const project of allProjects.values()) {
1512
- if (project.mf !== void 0) {
1513
- promises.push(
1514
- forEachMiniflare(project.mf, async (mf) => {
1515
- await waitForStorageReset(mf);
1516
- await mf.dispose();
1517
- })
1518
- );
1519
- }
1520
- }
1521
- allProjects.clear();
1522
- await Promise.all(promises);
1523
- }
1524
- };
1525
- }
1526
- export {
1527
- pool_default as default
1528
- };
1529
- //# sourceMappingURL=index.mjs.map