as-test 1.1.6 → 1.1.8

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 (42) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +4 -9
  3. package/assembly/index.ts +10 -15
  4. package/assembly/src/expectation.ts +11 -11
  5. package/assembly/src/fuzz.ts +11 -7
  6. package/assembly/src/log.ts +2 -2
  7. package/assembly/src/suite.ts +5 -5
  8. package/assembly/src/tests.ts +8 -8
  9. package/assembly/util/wipc.ts +5 -1
  10. package/bin/build-worker-pool.js +146 -142
  11. package/bin/build-worker.js +37 -34
  12. package/bin/commands/build-core.js +577 -465
  13. package/bin/commands/build.js +49 -29
  14. package/bin/commands/clean-core.js +120 -113
  15. package/bin/commands/clean.js +14 -8
  16. package/bin/commands/doctor-core.js +288 -289
  17. package/bin/commands/doctor.js +1 -1
  18. package/bin/commands/fuzz-core.js +467 -414
  19. package/bin/commands/fuzz.js +27 -10
  20. package/bin/commands/init-core.js +908 -794
  21. package/bin/commands/init.js +2 -2
  22. package/bin/commands/run-core.js +2675 -2344
  23. package/bin/commands/run.js +43 -25
  24. package/bin/commands/test.js +56 -32
  25. package/bin/commands/web-runner-source.js +1 -1
  26. package/bin/commands/web-session.js +516 -525
  27. package/bin/coverage-points.js +363 -341
  28. package/bin/crash-store.js +56 -66
  29. package/bin/index.js +4092 -3150
  30. package/bin/reporters/default.js +1090 -890
  31. package/bin/reporters/tap.js +319 -325
  32. package/bin/types.js +67 -67
  33. package/bin/util.js +1290 -1239
  34. package/bin/wipc.js +70 -73
  35. package/lib/build/index.d.ts +3 -1
  36. package/lib/build/index.js +1039 -1034
  37. package/lib/build/web-runner/client.js +1 -1
  38. package/lib/build/web-runner/html.js +1 -1
  39. package/lib/build/web-runner/worker.js +1 -1
  40. package/package.json +6 -3
  41. package/transform/lib/log.js +9 -5
  42. package/assembly/util/json.ts +0 -112
@@ -12,1155 +12,1160 @@ let patchedNodeIo = false;
12
12
  const wasiInstances = new WeakMap();
13
13
  const WEB_MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
14
14
  const WEB_HEADLESS_FLAGS = [
15
- "--headless=new",
16
- "--disable-gpu",
17
- "--no-first-run",
18
- "--no-default-browser-check",
15
+ "--headless=new",
16
+ "--disable-gpu",
17
+ "--no-first-run",
18
+ "--no-default-browser-check",
19
19
  ];
20
20
  function withNodeIo(imports) {
21
- validateImports(imports, "withNodeIo");
22
- patchNodeIo();
23
- return imports;
21
+ validateImports(imports, "withNodeIo");
22
+ patchNodeIo();
23
+ return imports;
24
24
  }
25
25
  export async function instantiate(imports) {
26
- validateImports(imports, "instantiate");
27
- const wasmPath = resolveWasmPath();
28
- const target = resolveRuntimeTarget();
29
- if (target == "wasi") {
30
- return instantiateWasiInstance(wasmPath, imports);
31
- }
32
- if (target == "web") {
33
- return instantiateWebInstance(wasmPath, imports);
34
- }
35
- const helperPath = resolveBindingsHelperPath(wasmPath);
36
- const kind = resolveBindingsKind(helperPath);
37
- if (kind == "raw") {
38
- return instantiateRawInstance(wasmPath, helperPath, imports);
39
- }
40
- if (kind == "esm") {
41
- return instantiateEsmInstance(wasmPath, helperPath, imports);
42
- }
43
- if (kind == "none") {
44
- return instantiateNoBindingsInstance(wasmPath, imports);
45
- }
46
- throw new Error(`unsupported bindings kind "${kind}"`);
26
+ validateImports(imports, "instantiate");
27
+ const wasmPath = resolveWasmPath();
28
+ const target = resolveRuntimeTarget();
29
+ if (target == "wasi") {
30
+ return instantiateWasiInstance(wasmPath, imports);
31
+ }
32
+ if (target == "web") {
33
+ return instantiateWebInstance(wasmPath, imports);
34
+ }
35
+ const helperPath = resolveBindingsHelperPath(wasmPath);
36
+ const kind = resolveBindingsKind(helperPath);
37
+ if (kind == "raw") {
38
+ return instantiateRawInstance(wasmPath, helperPath, imports);
39
+ }
40
+ if (kind == "esm") {
41
+ return instantiateEsmInstance(wasmPath, helperPath, imports);
42
+ }
43
+ if (kind == "none") {
44
+ return instantiateNoBindingsInstance(wasmPath, imports);
45
+ }
46
+ throw new Error(`unsupported bindings kind "${kind}"`);
47
47
  }
48
48
  function resolveRuntimeTarget() {
49
- const envTarget = process.env.AS_TEST_RUNTIME_TARGET?.trim();
50
- if (envTarget == "bindings" || envTarget == "wasi" || envTarget == "web") {
51
- return envTarget;
52
- }
53
- const runnerPath = String(process.argv[1] ?? "");
54
- if (runnerPath.includes(".wasi."))
55
- return "wasi";
56
- if (runnerPath.includes(".web."))
57
- return "web";
58
- return "bindings";
49
+ const envTarget = process.env.AS_TEST_RUNTIME_TARGET?.trim();
50
+ if (envTarget == "bindings" || envTarget == "wasi" || envTarget == "web") {
51
+ return envTarget;
52
+ }
53
+ const runnerPath = String(process.argv[1] ?? "");
54
+ if (runnerPath.includes(".wasi.")) return "wasi";
55
+ if (runnerPath.includes(".web.")) return "web";
56
+ return "bindings";
59
57
  }
60
58
  function resolveWasmPath() {
61
- const envWasmPath = process.env.AS_TEST_WASM_PATH?.trim();
62
- if (envWasmPath?.length) {
63
- return path.resolve(envWasmPath);
64
- }
65
- const argWasmPath = process.argv[2]?.trim();
66
- if (argWasmPath?.length) {
67
- return path.resolve(argWasmPath);
68
- }
69
- const runnerPath = String(process.argv[1] ?? "");
70
- const runnerName = path.basename(runnerPath || "runner.js");
71
- throw new Error([
72
- "No wasm artifact was provided for this runner.",
73
- "",
74
- `Direct usage: node .as-test/runners/${runnerName} .as-test/build/<artifact>.wasm`,
75
- "Managed usage: bunx ast test --mode <mode>",
76
- "",
77
- "as-test normally sets AS_TEST_WASM_PATH automatically when it launches the runner.",
78
- ].join("\n"));
59
+ const envWasmPath = process.env.AS_TEST_WASM_PATH?.trim();
60
+ if (envWasmPath?.length) {
61
+ return path.resolve(envWasmPath);
62
+ }
63
+ const argWasmPath = process.argv[2]?.trim();
64
+ if (argWasmPath?.length) {
65
+ return path.resolve(argWasmPath);
66
+ }
67
+ const runnerPath = String(process.argv[1] ?? "");
68
+ const runnerName = path.basename(runnerPath || "runner.js");
69
+ throw new Error(
70
+ [
71
+ "No wasm artifact was provided for this runner.",
72
+ "",
73
+ `Direct usage: node .as-test/runners/${runnerName} .as-test/build/<artifact>.wasm`,
74
+ "Managed usage: bunx ast test --mode <mode>",
75
+ "",
76
+ "as-test normally sets AS_TEST_WASM_PATH automatically when it launches the runner.",
77
+ ].join("\n"),
78
+ );
79
79
  }
80
80
  function resolveBindingsHelperPath(wasmPath) {
81
- const envHelperPath = process.env.AS_TEST_HELPER_PATH?.trim();
82
- if (envHelperPath?.length) {
83
- return path.resolve(envHelperPath);
84
- }
85
- const candidate = wasmPath.replace(/\.wasm$/i, ".js");
86
- return fs.existsSync(candidate) ? candidate : "";
81
+ const envHelperPath = process.env.AS_TEST_HELPER_PATH?.trim();
82
+ if (envHelperPath?.length) {
83
+ return path.resolve(envHelperPath);
84
+ }
85
+ const candidate = wasmPath.replace(/\.wasm$/i, ".js");
86
+ return fs.existsSync(candidate) ? candidate : "";
87
87
  }
88
88
  function resolveBindingsKind(helperPath) {
89
- const envKind = process.env.AS_TEST_BINDINGS_KIND?.trim();
90
- if (envKind == "raw" || envKind == "esm" || envKind == "none") {
91
- return envKind;
92
- }
93
- if (!helperPath.length) {
94
- return "none";
95
- }
96
- const source = fs.readFileSync(helperPath, "utf8");
97
- if (/\bexport\s+(?:async\s+)?function\s+instantiate\b/.test(source)) {
98
- return "raw";
99
- }
100
- return "esm";
89
+ const envKind = process.env.AS_TEST_BINDINGS_KIND?.trim();
90
+ if (envKind == "raw" || envKind == "esm" || envKind == "none") {
91
+ return envKind;
92
+ }
93
+ if (!helperPath.length) {
94
+ return "none";
95
+ }
96
+ const source = fs.readFileSync(helperPath, "utf8");
97
+ if (/\bexport\s+(?:async\s+)?function\s+instantiate\b/.test(source)) {
98
+ return "raw";
99
+ }
100
+ return "esm";
101
101
  }
102
102
  function validateImports(imports, fnName) {
103
- if (arguments.length < 1) {
104
- throw new Error(`${fnName}(imports) requires an imports object; pass {} when unused`);
105
- }
106
- if (!imports || typeof imports != "object" || Array.isArray(imports)) {
107
- throw new Error(`${fnName}(imports) requires a non-null imports object`);
108
- }
103
+ if (arguments.length < 1) {
104
+ throw new Error(
105
+ `${fnName}(imports) requires an imports object; pass {} when unused`,
106
+ );
107
+ }
108
+ if (!imports || typeof imports != "object" || Array.isArray(imports)) {
109
+ throw new Error(`${fnName}(imports) requires a non-null imports object`);
110
+ }
109
111
  }
110
112
  function patchNodeIo() {
111
- if (patchedNodeIo)
112
- return;
113
- patchedNodeIo = true;
114
- const originalWrite = process.stdout.write.bind(process.stdout);
115
- process.stdout.write = ((chunk, ...args) => {
116
- if (chunk instanceof ArrayBuffer) {
117
- writeRaw(chunk);
118
- return true;
119
- }
120
- return originalWrite(chunk, ...args);
121
- });
122
- process.stdin.read = ((size) => readExact(Number(size ?? 0)));
113
+ if (patchedNodeIo) return;
114
+ patchedNodeIo = true;
115
+ const originalWrite = process.stdout.write.bind(process.stdout);
116
+ process.stdout.write = (chunk, ...args) => {
117
+ if (chunk instanceof ArrayBuffer) {
118
+ writeRaw(chunk);
119
+ return true;
120
+ }
121
+ return originalWrite(chunk, ...args);
122
+ };
123
+ process.stdin.read = (size) => readExact(Number(size ?? 0));
123
124
  }
124
125
  function readExact(length) {
125
- const out = Buffer.alloc(length);
126
- let offset = 0;
127
- while (offset < length) {
128
- let read = 0;
129
- try {
130
- read = fs.readSync(0, out, offset, length - offset, null);
131
- }
132
- catch (error) {
133
- if (error &&
134
- typeof error == "object" &&
135
- "code" in error &&
136
- error.code == "EAGAIN") {
137
- continue;
138
- }
139
- throw error;
140
- }
141
- if (!read)
142
- break;
143
- offset += read;
126
+ const out = Buffer.alloc(length);
127
+ let offset = 0;
128
+ while (offset < length) {
129
+ let read = 0;
130
+ try {
131
+ read = fs.readSync(0, out, offset, length - offset, null);
132
+ } catch (error) {
133
+ if (
134
+ error &&
135
+ typeof error == "object" &&
136
+ "code" in error &&
137
+ error.code == "EAGAIN"
138
+ ) {
139
+ continue;
140
+ }
141
+ throw error;
144
142
  }
145
- const view = out.subarray(0, offset);
146
- return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
143
+ if (!read) break;
144
+ offset += read;
145
+ }
146
+ const view = out.subarray(0, offset);
147
+ return view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength);
147
148
  }
148
149
  function writeRaw(data) {
149
- const view = Buffer.from(data);
150
- let offset = 0;
151
- while (offset < view.byteLength) {
152
- let written = 0;
153
- try {
154
- written = fs.writeSync(1, view, offset, view.byteLength - offset);
155
- }
156
- catch (error) {
157
- if (error &&
158
- typeof error == "object" &&
159
- "code" in error &&
160
- error.code == "EAGAIN") {
161
- continue;
162
- }
163
- throw error;
164
- }
165
- if (!written)
166
- continue;
167
- offset += written;
150
+ const view = Buffer.from(data);
151
+ let offset = 0;
152
+ while (offset < view.byteLength) {
153
+ let written = 0;
154
+ try {
155
+ written = fs.writeSync(1, view, offset, view.byteLength - offset);
156
+ } catch (error) {
157
+ if (
158
+ error &&
159
+ typeof error == "object" &&
160
+ "code" in error &&
161
+ error.code == "EAGAIN"
162
+ ) {
163
+ continue;
164
+ }
165
+ throw error;
168
166
  }
167
+ if (!written) continue;
168
+ offset += written;
169
+ }
169
170
  }
170
171
  function mergeImports(...groups) {
171
- const out = {};
172
- for (const group of groups) {
173
- if (!group || typeof group != "object" || Array.isArray(group))
174
- continue;
175
- for (const [key, value] of Object.entries(group)) {
176
- if (value &&
177
- typeof value == "object" &&
178
- !Array.isArray(value) &&
179
- typeof value != "function") {
180
- out[key] = mergeImports(out[key], value);
181
- }
182
- else {
183
- out[key] = value;
184
- }
185
- }
172
+ const out = {};
173
+ for (const group of groups) {
174
+ if (!group || typeof group != "object" || Array.isArray(group)) continue;
175
+ for (const [key, value] of Object.entries(group)) {
176
+ if (
177
+ value &&
178
+ typeof value == "object" &&
179
+ !Array.isArray(value) &&
180
+ typeof value != "function"
181
+ ) {
182
+ out[key] = mergeImports(out[key], value);
183
+ } else {
184
+ out[key] = value;
185
+ }
186
186
  }
187
- return out;
187
+ }
188
+ return out;
188
189
  }
189
190
  async function instantiateRawInstance(wasmPath, helperPath, imports) {
190
- validateImports(imports, "instantiateRawInstance");
191
- if (!helperPath.length) {
192
- throw new Error("bindings kind is raw but AS_TEST_HELPER_PATH is not set");
193
- }
194
- const binary = fs.readFileSync(wasmPath);
195
- const module = new WebAssembly.Module(binary);
196
- const helper = (await import(`${pathToFileURL(helperPath).href}?t=${Date.now()}`));
197
- if (typeof helper.instantiate != "function") {
198
- throw new Error("bindings helper missing instantiate export");
199
- }
200
- const mergedImports = mergeImports(withNodeIo({}), imports);
201
- const instance = await captureHelperInstance(async () => {
202
- await helper.instantiate(module, mergedImports);
203
- });
204
- return decorateInstance(instance, "bindings");
191
+ validateImports(imports, "instantiateRawInstance");
192
+ if (!helperPath.length) {
193
+ throw new Error("bindings kind is raw but AS_TEST_HELPER_PATH is not set");
194
+ }
195
+ const binary = fs.readFileSync(wasmPath);
196
+ const module = new WebAssembly.Module(binary);
197
+ const helper = await import(
198
+ `${pathToFileURL(helperPath).href}?t=${Date.now()}`
199
+ );
200
+ if (typeof helper.instantiate != "function") {
201
+ throw new Error("bindings helper missing instantiate export");
202
+ }
203
+ const mergedImports = mergeImports(withNodeIo({}), imports);
204
+ const instance = await captureHelperInstance(async () => {
205
+ await helper.instantiate(module, mergedImports);
206
+ });
207
+ return decorateInstance(instance, "bindings");
205
208
  }
206
209
  async function instantiateEsmInstance(wasmPath, helperPath, imports) {
207
- validateImports(imports, "instantiateEsmInstance");
208
- if (!helperPath.length) {
209
- throw new Error("bindings kind is esm but AS_TEST_HELPER_PATH is not set");
210
- }
211
- if (hasUserImports(imports)) {
212
- throw new Error("esm bindings do not support custom imports in as-test/lib; pass {} or switch to raw bindings");
213
- }
214
- const instance = await captureHelperInstance(async () => {
215
- await import(`${pathToFileURL(helperPath).href}?t=${Date.now()}`);
216
- });
217
- return decorateInstance(instance, "bindings");
210
+ validateImports(imports, "instantiateEsmInstance");
211
+ if (!helperPath.length) {
212
+ throw new Error("bindings kind is esm but AS_TEST_HELPER_PATH is not set");
213
+ }
214
+ if (hasUserImports(imports)) {
215
+ throw new Error(
216
+ "esm bindings do not support custom imports in as-test/lib; pass {} or switch to raw bindings",
217
+ );
218
+ }
219
+ const instance = await captureHelperInstance(async () => {
220
+ await import(`${pathToFileURL(helperPath).href}?t=${Date.now()}`);
221
+ });
222
+ return decorateInstance(instance, "bindings");
218
223
  }
219
224
  async function instantiateNoBindingsInstance(wasmPath, imports) {
220
- validateImports(imports, "instantiateNoBindingsInstance");
221
- const instance = await instantiateModuleInstance(wasmPath, imports);
222
- return decorateInstance(instance, "bindings");
225
+ validateImports(imports, "instantiateNoBindingsInstance");
226
+ const instance = await instantiateModuleInstance(wasmPath, imports);
227
+ return decorateInstance(instance, "bindings");
223
228
  }
224
229
  async function instantiateWasiInstance(wasmPath, imports) {
225
- validateImports(imports, "instantiateWasiInstance");
226
- suppressExperimentalWasiWarning();
227
- const { WASI } = await import("wasi");
228
- const binary = fs.readFileSync(wasmPath);
229
- const module = new WebAssembly.Module(binary);
230
- const wasi = new WASI({
231
- version: "preview1",
232
- args: [wasmPath],
233
- env: process.env,
234
- preopens: {},
235
- });
236
- const mergedImports = createWasmImports(module, imports);
237
- mergedImports.wasi_snapshot_preview1 = wasi.wasiImport;
238
- const instance = new WebAssembly.Instance(module, mergedImports);
239
- wasiInstances.set(instance, wasi);
240
- return decorateInstance(instance, "wasi");
230
+ validateImports(imports, "instantiateWasiInstance");
231
+ suppressExperimentalWasiWarning();
232
+ const { WASI } = await import("wasi");
233
+ const binary = fs.readFileSync(wasmPath);
234
+ const module = new WebAssembly.Module(binary);
235
+ const wasi = new WASI({
236
+ version: "preview1",
237
+ args: [wasmPath],
238
+ env: process.env,
239
+ preopens: {},
240
+ });
241
+ const mergedImports = createWasmImports(module, imports);
242
+ mergedImports.wasi_snapshot_preview1 = wasi.wasiImport;
243
+ const instance = new WebAssembly.Instance(module, mergedImports);
244
+ wasiInstances.set(instance, wasi);
245
+ return decorateInstance(instance, "wasi");
241
246
  }
242
247
  async function instantiateWebInstance(wasmPath, imports) {
243
- validateImports(imports, "instantiateWebInstance");
244
- if (hasUserImports(imports)) {
245
- throw new Error("web runtime does not support custom imports in the default runner; pass {} or write a custom web runner");
246
- }
247
- const bindingsKind = (process.env.AS_TEST_BINDINGS_KIND ||
248
- "raw");
249
- const helperPath = process.env.AS_TEST_HELPER_PATH
250
- ? path.resolve(process.cwd(), process.env.AS_TEST_HELPER_PATH)
251
- : wasmPath.replace(/\.wasm$/, ".js");
252
- const wasmUrlPath = "/" + path.basename(wasmPath);
253
- const helperUrlPath = "/" + path.basename(helperPath);
254
- if (!fs.existsSync(wasmPath)) {
255
- throw new Error(`missing wasm artifact: ${wasmPath}`);
256
- }
257
- if (bindingsKind != "none" && !fs.existsSync(helperPath)) {
258
- throw new Error(`missing bindings helper: ${helperPath}`);
259
- }
260
- const html = buildWebRunnerHtml();
261
- const client = buildWebRunnerClientSource();
262
- const worker = buildWebRunnerWorkerSource();
263
- const headless = process.argv.includes("--headless");
264
- const webRuntimeEnv = {
265
- AS_TEST_RUNTIME_TARGET: "web",
266
- AS_TEST_WASM_PATH: wasmUrlPath,
267
- AS_TEST_BINDINGS_KIND: bindingsKind,
268
- ...(bindingsKind != "none" ? { AS_TEST_HELPER_PATH: helperUrlPath } : {}),
248
+ validateImports(imports, "instantiateWebInstance");
249
+ if (hasUserImports(imports)) {
250
+ throw new Error(
251
+ "web runtime does not support custom imports in the default runner; pass {} or write a custom web runner",
252
+ );
253
+ }
254
+ const bindingsKind = process.env.AS_TEST_BINDINGS_KIND || "raw";
255
+ const helperPath = process.env.AS_TEST_HELPER_PATH
256
+ ? path.resolve(process.cwd(), process.env.AS_TEST_HELPER_PATH)
257
+ : wasmPath.replace(/\.wasm$/, ".js");
258
+ const wasmUrlPath = "/" + path.basename(wasmPath);
259
+ const helperUrlPath = "/" + path.basename(helperPath);
260
+ if (!fs.existsSync(wasmPath)) {
261
+ throw new Error(`missing wasm artifact: ${wasmPath}`);
262
+ }
263
+ if (bindingsKind != "none" && !fs.existsSync(helperPath)) {
264
+ throw new Error(`missing bindings helper: ${helperPath}`);
265
+ }
266
+ const html = buildWebRunnerHtml();
267
+ const client = buildWebRunnerClientSource();
268
+ const worker = buildWebRunnerWorkerSource();
269
+ const headless = process.argv.includes("--headless");
270
+ const webRuntimeEnv = {
271
+ AS_TEST_RUNTIME_TARGET: "web",
272
+ AS_TEST_WASM_PATH: wasmUrlPath,
273
+ AS_TEST_BINDINGS_KIND: bindingsKind,
274
+ ...(bindingsKind != "none" ? { AS_TEST_HELPER_PATH: helperUrlPath } : {}),
275
+ };
276
+ return new Promise((resolve, reject) => {
277
+ let resolved = false;
278
+ let finished = false;
279
+ let ready = false;
280
+ let wsSocket = null;
281
+ let wsBuffer = Buffer.alloc(0);
282
+ let stdinBuffer = Buffer.alloc(0);
283
+ let browserProcess = null;
284
+ let browserStderr = "";
285
+ let browserRetryTimer = null;
286
+ let browserStartupTimer = null;
287
+ let browserTempProfileDir = null;
288
+ let ownsBrowserProcess = false;
289
+ const pendingFrames = [];
290
+ const rejectOnce = (error) => {
291
+ if (resolved || finished) return;
292
+ finished = true;
293
+ reject(error);
294
+ cleanup();
269
295
  };
270
- return new Promise((resolve, reject) => {
271
- let resolved = false;
272
- let finished = false;
273
- let ready = false;
274
- let wsSocket = null;
275
- let wsBuffer = Buffer.alloc(0);
276
- let stdinBuffer = Buffer.alloc(0);
277
- let browserProcess = null;
278
- let browserStderr = "";
279
- let browserRetryTimer = null;
280
- let browserStartupTimer = null;
281
- let browserTempProfileDir = null;
282
- let ownsBrowserProcess = false;
283
- const pendingFrames = [];
284
- const rejectOnce = (error) => {
285
- if (resolved || finished)
286
- return;
287
- finished = true;
288
- reject(error);
289
- cleanup();
290
- };
291
- const finish = (code) => {
292
- if (finished)
293
- return;
294
- finished = true;
295
- cleanup();
296
- if (!resolved && code != 0) {
297
- reject(new Error(`web runtime exited with code ${code}`));
298
- return;
299
- }
300
- if (!resolved) {
301
- reject(new Error("web runtime exited before instantiation completed"));
302
- }
303
- };
304
- const cleanup = () => {
305
- process.stdin.off("data", onStdinData);
306
- process.stdin.off("end", onStdinEnd);
307
- try {
308
- process.stdin.pause();
309
- }
310
- catch { }
311
- process.off("SIGINT", onSigint);
312
- process.off("SIGTERM", onSigterm);
313
- try {
314
- wsSocket?.end();
315
- }
316
- catch { }
317
- try {
318
- wsSocket?.destroy();
319
- }
320
- catch { }
321
- try {
322
- server.close();
323
- }
324
- catch { }
325
- try {
326
- server.unref();
327
- }
328
- catch { }
329
- if (browserRetryTimer) {
330
- clearInterval(browserRetryTimer);
331
- browserRetryTimer = null;
332
- }
333
- if (browserStartupTimer) {
334
- clearTimeout(browserStartupTimer);
335
- browserStartupTimer = null;
336
- }
337
- if (browserTempProfileDir) {
338
- try {
339
- fs.rmSync(browserTempProfileDir, { recursive: true, force: true });
340
- }
341
- catch { }
342
- browserTempProfileDir = null;
343
- }
344
- if (browserProcess && ownsBrowserProcess && !browserProcess.killed) {
345
- killOwnedBrowserProcess(browserProcess);
346
- }
347
- };
348
- const sendControl = (message) => {
349
- if (!wsSocket)
350
- return;
351
- sendWebSocketFrame(wsSocket, 0x1, Buffer.from(JSON.stringify(message)));
352
- };
353
- const flushPendingFrames = () => {
354
- if (!ready || !wsSocket)
355
- return;
356
- while (pendingFrames.length) {
357
- sendWebSocketFrame(wsSocket, 0x2, pendingFrames.shift());
358
- }
359
- };
360
- const onControl = (raw) => {
361
- let message = null;
362
- try {
363
- message = JSON.parse(raw);
364
- }
365
- catch {
366
- return;
367
- }
368
- if (message?.kind == "ready") {
369
- ready = true;
370
- flushPendingFrames();
371
- return;
372
- }
373
- if (message?.kind == "instantiated") {
374
- if (resolved)
375
- return;
376
- resolved = true;
377
- resolve(createWebInstanceController(() => {
378
- sendControl({ kind: "start" });
379
- }));
380
- return;
381
- }
382
- if (message?.kind == "done") {
383
- finish(0);
384
- return;
385
- }
386
- if (message?.kind == "error") {
387
- rejectOnce(new Error(String(message.message ?? "browser runtime failed")));
388
- }
389
- };
390
- const onWebSocketData = (chunk) => {
391
- wsBuffer = Buffer.concat([wsBuffer, chunk]);
392
- while (wsBuffer.length >= 2) {
393
- const first = wsBuffer[0];
394
- const second = wsBuffer[1];
395
- const opcode = first & 0x0f;
396
- const masked = (second & 0x80) !== 0;
397
- let length = second & 0x7f;
398
- let offset = 2;
399
- if (length == 126) {
400
- if (wsBuffer.length < offset + 2)
401
- return;
402
- length = wsBuffer.readUInt16BE(offset);
403
- offset += 2;
404
- }
405
- else if (length == 127) {
406
- if (wsBuffer.length < offset + 8)
407
- return;
408
- length = Number(wsBuffer.readBigUInt64BE(offset));
409
- offset += 8;
410
- }
411
- const maskLength = masked ? 4 : 0;
412
- if (wsBuffer.length < offset + maskLength + length)
413
- return;
414
- let payload = wsBuffer.subarray(offset + maskLength, offset + maskLength + length);
415
- if (masked) {
416
- const mask = wsBuffer.subarray(offset, offset + 4);
417
- const unmasked = Buffer.alloc(length);
418
- for (let i = 0; i < length; i++) {
419
- unmasked[i] = payload[i] ^ mask[i % 4];
420
- }
421
- payload = unmasked;
422
- }
423
- else {
424
- payload = Buffer.from(payload);
425
- }
426
- wsBuffer = wsBuffer.subarray(offset + maskLength + length);
427
- if (opcode == 0x8) {
428
- finish(0);
429
- return;
430
- }
431
- if (opcode == 0x1) {
432
- onControl(payload.toString("utf8"));
433
- continue;
434
- }
435
- if (opcode == 0x2) {
436
- process.stdout.write(payload);
437
- }
438
- }
439
- };
440
- const onStdinData = (chunk) => {
441
- stdinBuffer = Buffer.concat([stdinBuffer, chunk]);
442
- while (stdinBuffer.length >= 9) {
443
- const length = stdinBuffer.readUInt32LE(5);
444
- const frameSize = 9 + length;
445
- if (stdinBuffer.length < frameSize)
446
- return;
447
- const frame = stdinBuffer.subarray(0, frameSize);
448
- stdinBuffer = stdinBuffer.subarray(frameSize);
449
- if (ready && wsSocket) {
450
- sendWebSocketFrame(wsSocket, 0x2, frame);
451
- }
452
- else {
453
- pendingFrames.push(Buffer.from(frame));
454
- }
455
- }
456
- };
457
- const onStdinEnd = () => {
458
- stdinBuffer = Buffer.alloc(0);
459
- };
460
- const onSigint = () => finish(130);
461
- const onSigterm = () => finish(143);
462
- const server = http.createServer((req, res) => {
463
- const headers = {
464
- "Cross-Origin-Embedder-Policy": "require-corp",
465
- "Cross-Origin-Opener-Policy": "same-origin",
466
- "Cache-Control": "no-store",
467
- };
468
- const url = req.url ?? "/";
469
- if (url == "/" || url.startsWith("/?")) {
470
- res.writeHead(200, {
471
- ...headers,
472
- "Content-Type": "text/html; charset=utf-8",
473
- });
474
- res.end(html.replace("</body>", " <script>window.__AS_TEST_ENV__ = " +
475
- JSON.stringify(webRuntimeEnv) +
476
- ";</script>\n </body>"));
477
- return;
478
- }
479
- if (url == "/client.js") {
480
- res.writeHead(200, {
481
- ...headers,
482
- "Content-Type": "text/javascript; charset=utf-8",
483
- });
484
- res.end(client);
485
- return;
486
- }
487
- if (url == "/worker.js") {
488
- res.writeHead(200, {
489
- ...headers,
490
- "Content-Type": "text/javascript; charset=utf-8",
491
- });
492
- res.end(worker);
493
- return;
494
- }
495
- if (url == helperUrlPath) {
496
- if (bindingsKind == "none") {
497
- res.writeHead(404, headers);
498
- res.end("not found");
499
- return;
500
- }
501
- res.writeHead(200, {
502
- ...headers,
503
- "Content-Type": "text/javascript; charset=utf-8",
504
- });
505
- res.end(fs.readFileSync(helperPath, "utf8"));
506
- return;
507
- }
508
- if (url == wasmUrlPath) {
509
- res.writeHead(200, {
510
- ...headers,
511
- "Content-Type": "application/wasm",
512
- });
513
- res.end(fs.readFileSync(wasmPath));
514
- return;
515
- }
516
- res.writeHead(404, headers);
517
- res.end("not found");
296
+ const finish = (code) => {
297
+ if (finished) return;
298
+ finished = true;
299
+ cleanup();
300
+ if (!resolved && code != 0) {
301
+ reject(new Error(`web runtime exited with code ${code}`));
302
+ return;
303
+ }
304
+ if (!resolved) {
305
+ reject(new Error("web runtime exited before instantiation completed"));
306
+ }
307
+ };
308
+ const cleanup = () => {
309
+ process.stdin.off("data", onStdinData);
310
+ process.stdin.off("end", onStdinEnd);
311
+ try {
312
+ process.stdin.pause();
313
+ } catch {}
314
+ process.off("SIGINT", onSigint);
315
+ process.off("SIGTERM", onSigterm);
316
+ try {
317
+ wsSocket?.end();
318
+ } catch {}
319
+ try {
320
+ wsSocket?.destroy();
321
+ } catch {}
322
+ try {
323
+ server.close();
324
+ } catch {}
325
+ try {
326
+ server.unref();
327
+ } catch {}
328
+ if (browserRetryTimer) {
329
+ clearInterval(browserRetryTimer);
330
+ browserRetryTimer = null;
331
+ }
332
+ if (browserStartupTimer) {
333
+ clearTimeout(browserStartupTimer);
334
+ browserStartupTimer = null;
335
+ }
336
+ if (browserTempProfileDir) {
337
+ try {
338
+ fs.rmSync(browserTempProfileDir, { recursive: true, force: true });
339
+ } catch {}
340
+ browserTempProfileDir = null;
341
+ }
342
+ if (browserProcess && ownsBrowserProcess && !browserProcess.killed) {
343
+ killOwnedBrowserProcess(browserProcess);
344
+ }
345
+ };
346
+ const sendControl = (message) => {
347
+ if (!wsSocket) return;
348
+ sendWebSocketFrame(wsSocket, 0x1, Buffer.from(JSON.stringify(message)));
349
+ };
350
+ const flushPendingFrames = () => {
351
+ if (!ready || !wsSocket) return;
352
+ while (pendingFrames.length) {
353
+ sendWebSocketFrame(wsSocket, 0x2, pendingFrames.shift());
354
+ }
355
+ };
356
+ const onControl = (raw) => {
357
+ let message = null;
358
+ try {
359
+ message = JSON.parse(raw);
360
+ } catch {
361
+ return;
362
+ }
363
+ if (message?.kind == "ready") {
364
+ ready = true;
365
+ flushPendingFrames();
366
+ return;
367
+ }
368
+ if (message?.kind == "instantiated") {
369
+ if (resolved) return;
370
+ resolved = true;
371
+ resolve(
372
+ createWebInstanceController(() => {
373
+ sendControl({ kind: "start" });
374
+ }),
375
+ );
376
+ return;
377
+ }
378
+ if (message?.kind == "done") {
379
+ finish(0);
380
+ return;
381
+ }
382
+ if (message?.kind == "error") {
383
+ rejectOnce(
384
+ new Error(String(message.message ?? "browser runtime failed")),
385
+ );
386
+ }
387
+ };
388
+ const onWebSocketData = (chunk) => {
389
+ wsBuffer = Buffer.concat([wsBuffer, chunk]);
390
+ while (wsBuffer.length >= 2) {
391
+ const first = wsBuffer[0];
392
+ const second = wsBuffer[1];
393
+ const opcode = first & 0x0f;
394
+ const masked = (second & 0x80) !== 0;
395
+ let length = second & 0x7f;
396
+ let offset = 2;
397
+ if (length == 126) {
398
+ if (wsBuffer.length < offset + 2) return;
399
+ length = wsBuffer.readUInt16BE(offset);
400
+ offset += 2;
401
+ } else if (length == 127) {
402
+ if (wsBuffer.length < offset + 8) return;
403
+ length = Number(wsBuffer.readBigUInt64BE(offset));
404
+ offset += 8;
405
+ }
406
+ const maskLength = masked ? 4 : 0;
407
+ if (wsBuffer.length < offset + maskLength + length) return;
408
+ let payload = wsBuffer.subarray(
409
+ offset + maskLength,
410
+ offset + maskLength + length,
411
+ );
412
+ if (masked) {
413
+ const mask = wsBuffer.subarray(offset, offset + 4);
414
+ const unmasked = Buffer.alloc(length);
415
+ for (let i = 0; i < length; i++) {
416
+ unmasked[i] = payload[i] ^ mask[i % 4];
417
+ }
418
+ payload = unmasked;
419
+ } else {
420
+ payload = Buffer.from(payload);
421
+ }
422
+ wsBuffer = wsBuffer.subarray(offset + maskLength + length);
423
+ if (opcode == 0x8) {
424
+ finish(0);
425
+ return;
426
+ }
427
+ if (opcode == 0x1) {
428
+ onControl(payload.toString("utf8"));
429
+ continue;
430
+ }
431
+ if (opcode == 0x2) {
432
+ process.stdout.write(payload);
433
+ }
434
+ }
435
+ };
436
+ const onStdinData = (chunk) => {
437
+ stdinBuffer = Buffer.concat([stdinBuffer, chunk]);
438
+ while (stdinBuffer.length >= 9) {
439
+ const length = stdinBuffer.readUInt32LE(5);
440
+ const frameSize = 9 + length;
441
+ if (stdinBuffer.length < frameSize) return;
442
+ const frame = stdinBuffer.subarray(0, frameSize);
443
+ stdinBuffer = stdinBuffer.subarray(frameSize);
444
+ if (ready && wsSocket) {
445
+ sendWebSocketFrame(wsSocket, 0x2, frame);
446
+ } else {
447
+ pendingFrames.push(Buffer.from(frame));
448
+ }
449
+ }
450
+ };
451
+ const onStdinEnd = () => {
452
+ stdinBuffer = Buffer.alloc(0);
453
+ };
454
+ const onSigint = () => finish(130);
455
+ const onSigterm = () => finish(143);
456
+ const server = http.createServer((req, res) => {
457
+ const headers = {
458
+ "Cross-Origin-Embedder-Policy": "require-corp",
459
+ "Cross-Origin-Opener-Policy": "same-origin",
460
+ "Cache-Control": "no-store",
461
+ };
462
+ const url = req.url ?? "/";
463
+ if (url == "/" || url.startsWith("/?")) {
464
+ res.writeHead(200, {
465
+ ...headers,
466
+ "Content-Type": "text/html; charset=utf-8",
518
467
  });
519
- server.on("upgrade", (req, socket) => {
520
- if ((req.url ?? "") != "/ws") {
521
- socket.destroy();
522
- return;
523
- }
524
- const key = String(req.headers["sec-websocket-key"] ?? "");
525
- if (!key) {
526
- socket.destroy();
527
- return;
528
- }
529
- const accept = createHash("sha1")
530
- .update(key + WEB_MAGIC)
531
- .digest("base64");
532
- socket.write([
533
- "HTTP/1.1 101 Switching Protocols",
534
- "Upgrade: websocket",
535
- "Connection: Upgrade",
536
- "Sec-WebSocket-Accept: " + accept,
537
- "",
538
- "",
539
- ].join("\r\n"));
540
- wsSocket = socket;
541
- wsBuffer = Buffer.alloc(0);
542
- if (browserStartupTimer) {
543
- clearTimeout(browserStartupTimer);
544
- browserStartupTimer = null;
545
- }
546
- socket.on("data", (chunk) => onWebSocketData(chunk));
547
- socket.on("close", () => {
548
- wsSocket = null;
549
- if (!finished)
550
- finish(1);
551
- });
552
- socket.on("error", (error) => {
553
- if (!finished) {
554
- rejectOnce(error instanceof Error ? error : new Error(String(error)));
555
- }
556
- });
557
- flushPendingFrames();
468
+ res.end(
469
+ html.replace(
470
+ "</body>",
471
+ " <script>window.__AS_TEST_ENV__ = " +
472
+ JSON.stringify(webRuntimeEnv) +
473
+ ";</script>\n </body>",
474
+ ),
475
+ );
476
+ return;
477
+ }
478
+ if (url == "/client.js") {
479
+ res.writeHead(200, {
480
+ ...headers,
481
+ "Content-Type": "text/javascript; charset=utf-8",
558
482
  });
559
- process.stdin.on("data", onStdinData);
560
- process.stdin.on("end", onStdinEnd);
561
- process.on("SIGINT", onSigint);
562
- process.on("SIGTERM", onSigterm);
563
- server.listen(0, "127.0.0.1", () => {
564
- const address = server.address();
565
- if (!address || typeof address == "string") {
566
- rejectOnce(new Error("failed to determine local web runner address"));
567
- return;
568
- }
569
- const url = "http://127.0.0.1:" + address.port + "/";
483
+ res.end(client);
484
+ return;
485
+ }
486
+ if (url == "/worker.js") {
487
+ res.writeHead(200, {
488
+ ...headers,
489
+ "Content-Type": "text/javascript; charset=utf-8",
490
+ });
491
+ res.end(worker);
492
+ return;
493
+ }
494
+ if (url == helperUrlPath) {
495
+ if (bindingsKind == "none") {
496
+ res.writeHead(404, headers);
497
+ res.end("not found");
498
+ return;
499
+ }
500
+ res.writeHead(200, {
501
+ ...headers,
502
+ "Content-Type": "text/javascript; charset=utf-8",
503
+ });
504
+ res.end(fs.readFileSync(helperPath, "utf8"));
505
+ return;
506
+ }
507
+ if (url == wasmUrlPath) {
508
+ res.writeHead(200, {
509
+ ...headers,
510
+ "Content-Type": "application/wasm",
511
+ });
512
+ res.end(fs.readFileSync(wasmPath));
513
+ return;
514
+ }
515
+ res.writeHead(404, headers);
516
+ res.end("not found");
517
+ });
518
+ server.on("upgrade", (req, socket) => {
519
+ if ((req.url ?? "") != "/ws") {
520
+ socket.destroy();
521
+ return;
522
+ }
523
+ const key = String(req.headers["sec-websocket-key"] ?? "");
524
+ if (!key) {
525
+ socket.destroy();
526
+ return;
527
+ }
528
+ const accept = createHash("sha1")
529
+ .update(key + WEB_MAGIC)
530
+ .digest("base64");
531
+ socket.write(
532
+ [
533
+ "HTTP/1.1 101 Switching Protocols",
534
+ "Upgrade: websocket",
535
+ "Connection: Upgrade",
536
+ "Sec-WebSocket-Accept: " + accept,
537
+ "",
538
+ "",
539
+ ].join("\r\n"),
540
+ );
541
+ wsSocket = socket;
542
+ wsBuffer = Buffer.alloc(0);
543
+ if (browserStartupTimer) {
544
+ clearTimeout(browserStartupTimer);
545
+ browserStartupTimer = null;
546
+ }
547
+ socket.on("data", (chunk) => onWebSocketData(chunk));
548
+ socket.on("close", () => {
549
+ wsSocket = null;
550
+ if (!finished) finish(1);
551
+ });
552
+ socket.on("error", (error) => {
553
+ if (!finished) {
554
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
555
+ }
556
+ });
557
+ flushPendingFrames();
558
+ });
559
+ process.stdin.on("data", onStdinData);
560
+ process.stdin.on("end", onStdinEnd);
561
+ process.on("SIGINT", onSigint);
562
+ process.on("SIGTERM", onSigterm);
563
+ server.listen(0, "127.0.0.1", () => {
564
+ const address = server.address();
565
+ if (!address || typeof address == "string") {
566
+ rejectOnce(new Error("failed to determine local web runner address"));
567
+ return;
568
+ }
569
+ const url = "http://127.0.0.1:" + address.port + "/";
570
+ try {
571
+ const launched = launchWebBrowser(url, headless);
572
+ browserProcess = launched.process;
573
+ browserTempProfileDir = launched.tempProfileDir;
574
+ ownsBrowserProcess = launched.ownsProcess;
575
+ if (browserProcess.stderr) {
576
+ browserProcess.stderr.on("data", (chunk) => {
577
+ browserStderr = appendBrowserOutput(
578
+ browserStderr,
579
+ typeof chunk == "string" ? chunk : chunk.toString("utf8"),
580
+ );
581
+ });
582
+ }
583
+ if (!headless) {
584
+ browserRetryTimer = setInterval(() => {
585
+ if (finished || resolved || ready || wsSocket) return;
570
586
  try {
571
- const launched = launchWebBrowser(url, headless);
572
- browserProcess = launched.process;
573
- browserTempProfileDir = launched.tempProfileDir;
574
- ownsBrowserProcess = launched.ownsProcess;
575
- if (browserProcess.stderr) {
576
- browserProcess.stderr.on("data", (chunk) => {
577
- browserStderr = appendBrowserOutput(browserStderr, typeof chunk == "string" ? chunk : chunk.toString("utf8"));
578
- });
579
- }
580
- if (!headless) {
581
- browserRetryTimer = setInterval(() => {
582
- if (finished || resolved || ready || wsSocket)
583
- return;
584
- try {
585
- openWithReusableBrowserWindow(url);
586
- }
587
- catch { }
588
- }, 750);
589
- browserRetryTimer.unref?.();
590
- }
591
- if (headless) {
592
- browserStartupTimer = setTimeout(() => {
593
- if (finished || resolved || ready || wsSocket)
594
- return;
595
- rejectOnce(new Error("headless web browser did not connect to the local runner"));
596
- }, 10000);
597
- browserStartupTimer.unref?.();
598
- browserProcess.on("close", (code) => {
599
- if (finished)
600
- return;
601
- if (resolved) {
602
- finish(code ?? 0);
603
- return;
604
- }
605
- if (code && code != 0) {
606
- rejectOnce(new Error(formatBrowserExitError(code, browserStderr)));
607
- return;
608
- }
609
- if (ready || wsSocket) {
610
- finish(code ?? 0);
611
- }
612
- });
613
- }
587
+ openWithReusableBrowserWindow(url);
588
+ } catch {}
589
+ }, 750);
590
+ browserRetryTimer.unref?.();
591
+ }
592
+ if (headless) {
593
+ browserStartupTimer = setTimeout(() => {
594
+ if (finished || resolved || ready || wsSocket) return;
595
+ rejectOnce(
596
+ new Error(
597
+ "headless web browser did not connect to the local runner",
598
+ ),
599
+ );
600
+ }, 10000);
601
+ browserStartupTimer.unref?.();
602
+ browserProcess.on("close", (code) => {
603
+ if (finished) return;
604
+ if (resolved) {
605
+ finish(code ?? 0);
606
+ return;
614
607
  }
615
- catch (error) {
616
- rejectOnce(error instanceof Error ? error : new Error(String(error)));
608
+ if (code && code != 0) {
609
+ rejectOnce(
610
+ new Error(formatBrowserExitError(code, browserStderr)),
611
+ );
612
+ return;
617
613
  }
618
- });
614
+ if (ready || wsSocket) {
615
+ finish(code ?? 0);
616
+ }
617
+ });
618
+ }
619
+ } catch (error) {
620
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
621
+ }
619
622
  });
623
+ });
620
624
  }
621
625
  function hasUserImports(imports) {
622
- return Object.keys(imports).length > 0;
626
+ return Object.keys(imports).length > 0;
623
627
  }
624
628
  function createWasmImports(module, imports) {
625
- const mergedImports = mergeImports(withNodeIo({}), imports);
626
- if (!mergedImports.env || typeof mergedImports.env != "object") {
627
- mergedImports.env = {};
628
- }
629
- for (const entry of WebAssembly.Module.imports(module)) {
630
- if (entry.module == "env" &&
631
- entry.kind == "function" &&
632
- !(entry.name in mergedImports.env)) {
633
- mergedImports.env[entry.name] = () => 0;
634
- }
629
+ const mergedImports = mergeImports(withNodeIo({}), imports);
630
+ if (!mergedImports.env || typeof mergedImports.env != "object") {
631
+ mergedImports.env = {};
632
+ }
633
+ for (const entry of WebAssembly.Module.imports(module)) {
634
+ if (
635
+ entry.module == "env" &&
636
+ entry.kind == "function" &&
637
+ !(entry.name in mergedImports.env)
638
+ ) {
639
+ mergedImports.env[entry.name] = () => 0;
635
640
  }
636
- return mergedImports;
641
+ }
642
+ return mergedImports;
637
643
  }
638
644
  let patchedWasiWarning = false;
639
645
  function suppressExperimentalWasiWarning() {
640
- if (patchedWasiWarning)
641
- return;
642
- patchedWasiWarning = true;
643
- const originalEmitWarning = process.emitWarning.bind(process);
644
- process.emitWarning = ((warning, ...args) => {
645
- const type = typeof args[0] == "string" ? args[0] : "";
646
- const name = warning && typeof warning == "object" && "name" in warning
647
- ? String(warning.name ?? type)
648
- : type;
649
- const message = typeof warning == "string"
650
- ? warning
651
- : String(warning && typeof warning == "object" && "message" in warning
652
- ? (warning.message ?? "")
653
- : "");
654
- if (name == "ExperimentalWarning" &&
655
- message.includes("WASI is an experimental feature")) {
656
- return;
657
- }
658
- return originalEmitWarning(warning, ...args);
659
- });
646
+ if (patchedWasiWarning) return;
647
+ patchedWasiWarning = true;
648
+ const originalEmitWarning = process.emitWarning.bind(process);
649
+ process.emitWarning = (warning, ...args) => {
650
+ const type = typeof args[0] == "string" ? args[0] : "";
651
+ const name =
652
+ warning && typeof warning == "object" && "name" in warning
653
+ ? String(warning.name ?? type)
654
+ : type;
655
+ const message =
656
+ typeof warning == "string"
657
+ ? warning
658
+ : String(
659
+ warning && typeof warning == "object" && "message" in warning
660
+ ? (warning.message ?? "")
661
+ : "",
662
+ );
663
+ if (
664
+ name == "ExperimentalWarning" &&
665
+ message.includes("WASI is an experimental feature")
666
+ ) {
667
+ return;
668
+ }
669
+ return originalEmitWarning(warning, ...args);
670
+ };
660
671
  }
661
672
  async function instantiateModuleInstance(wasmPath, imports) {
662
- const binary = fs.readFileSync(wasmPath);
663
- const module = new WebAssembly.Module(binary);
664
- return new WebAssembly.Instance(module, createWasmImports(module, imports));
673
+ const binary = fs.readFileSync(wasmPath);
674
+ const module = new WebAssembly.Module(binary);
675
+ return new WebAssembly.Instance(module, createWasmImports(module, imports));
665
676
  }
666
677
  function decorateInstance(instance, target) {
667
- const exports = instance.exports;
668
- const start = createStartFunction(instance, target, exports);
669
- if (!start)
670
- return instance;
671
- const exportsProxy = new Proxy(exports, {
672
- get(targetExports, prop, receiver) {
673
- if (prop == "start")
674
- return start;
675
- return Reflect.get(targetExports, prop, receiver);
676
- },
677
- has(targetExports, prop) {
678
- if (prop == "start")
679
- return true;
680
- return Reflect.has(targetExports, prop);
681
- },
682
- });
683
- return new Proxy(instance, {
684
- get(targetInstance, prop, receiver) {
685
- if (prop == "exports")
686
- return exportsProxy;
687
- return Reflect.get(targetInstance, prop, receiver);
688
- },
689
- });
678
+ const exports = instance.exports;
679
+ const start = createStartFunction(instance, target, exports);
680
+ if (!start) return instance;
681
+ const exportsProxy = new Proxy(exports, {
682
+ get(targetExports, prop, receiver) {
683
+ if (prop == "start") return start;
684
+ return Reflect.get(targetExports, prop, receiver);
685
+ },
686
+ has(targetExports, prop) {
687
+ if (prop == "start") return true;
688
+ return Reflect.has(targetExports, prop);
689
+ },
690
+ });
691
+ return new Proxy(instance, {
692
+ get(targetInstance, prop, receiver) {
693
+ if (prop == "exports") return exportsProxy;
694
+ return Reflect.get(targetInstance, prop, receiver);
695
+ },
696
+ });
690
697
  }
691
698
  function createStartFunction(instance, target, exports) {
692
- if (target == "wasi") {
693
- return () => {
694
- const wasi = wasiInstances.get(instance);
695
- if (!wasi) {
696
- throw new Error("WASI runtime state missing for instance");
697
- }
698
- wasi.start(instance);
699
- };
700
- }
701
- const startFn = exports._start;
702
- if (typeof startFn != "function") {
703
- return null;
704
- }
699
+ if (target == "wasi") {
705
700
  return () => {
706
- startFn();
701
+ const wasi = wasiInstances.get(instance);
702
+ if (!wasi) {
703
+ throw new Error("WASI runtime state missing for instance");
704
+ }
705
+ wasi.start(instance);
707
706
  };
707
+ }
708
+ const startFn = exports._start;
709
+ if (typeof startFn != "function") {
710
+ return null;
711
+ }
712
+ return () => {
713
+ startFn();
714
+ };
708
715
  }
709
716
  function createWebInstanceController(start) {
710
- const exportsProxy = new Proxy({}, {
711
- get(_target, prop) {
712
- if (prop == "start")
713
- return start;
714
- return undefined;
715
- },
716
- has(_target, prop) {
717
- return prop == "start";
718
- },
719
- });
720
- return new Proxy({}, {
721
- get(_target, prop) {
722
- if (prop == "exports")
723
- return exportsProxy;
724
- return undefined;
725
- },
726
- has(_target, prop) {
727
- return prop == "exports";
728
- },
729
- });
717
+ const exportsProxy = new Proxy(
718
+ {},
719
+ {
720
+ get(_target, prop) {
721
+ if (prop == "start") return start;
722
+ return undefined;
723
+ },
724
+ has(_target, prop) {
725
+ return prop == "start";
726
+ },
727
+ },
728
+ );
729
+ return new Proxy(
730
+ {},
731
+ {
732
+ get(_target, prop) {
733
+ if (prop == "exports") return exportsProxy;
734
+ return undefined;
735
+ },
736
+ has(_target, prop) {
737
+ return prop == "exports";
738
+ },
739
+ },
740
+ );
730
741
  }
731
742
  function sendWebSocketFrame(socket, opcode, payload) {
732
- let header;
733
- if (payload.length < 126) {
734
- header = Buffer.from([0x80 | opcode, payload.length]);
735
- }
736
- else if (payload.length < 65536) {
737
- header = Buffer.alloc(4);
738
- header[0] = 0x80 | opcode;
739
- header[1] = 126;
740
- header.writeUInt16BE(payload.length, 2);
741
- }
742
- else {
743
- header = Buffer.alloc(10);
744
- header[0] = 0x80 | opcode;
745
- header[1] = 127;
746
- header.writeBigUInt64BE(BigInt(payload.length), 2);
747
- }
748
- socket.write(Buffer.concat([header, payload]));
743
+ let header;
744
+ if (payload.length < 126) {
745
+ header = Buffer.from([0x80 | opcode, payload.length]);
746
+ } else if (payload.length < 65536) {
747
+ header = Buffer.alloc(4);
748
+ header[0] = 0x80 | opcode;
749
+ header[1] = 126;
750
+ header.writeUInt16BE(payload.length, 2);
751
+ } else {
752
+ header = Buffer.alloc(10);
753
+ header[0] = 0x80 | opcode;
754
+ header[1] = 127;
755
+ header.writeBigUInt64BE(BigInt(payload.length), 2);
756
+ }
757
+ socket.write(Buffer.concat([header, payload]));
749
758
  }
750
759
  function launchWebBrowser(url, headless) {
751
- if (!headless) {
752
- const reused = openWithReusableBrowserWindow(url);
753
- if (reused) {
754
- return { process: reused, tempProfileDir: null, ownsProcess: false };
755
- }
756
- const opener = openWithSystemBrowser(url);
757
- if (opener) {
758
- return { process: opener, tempProfileDir: null, ownsProcess: false };
759
- }
760
+ if (!headless) {
761
+ const reused = openWithReusableBrowserWindow(url);
762
+ if (reused) {
763
+ return { process: reused, tempProfileDir: null, ownsProcess: false };
764
+ }
765
+ const opener = openWithSystemBrowser(url);
766
+ if (opener) {
767
+ return { process: opener, tempProfileDir: null, ownsProcess: false };
760
768
  }
761
- const direct = openWithInstalledBrowser(url, headless);
762
- if (direct)
763
- return direct;
764
- throw new Error(headless
765
- ? "could not find a headless-capable browser; set BROWSER to a Chromium/Firefox executable"
766
- : "could not open a browser automatically; set BROWSER to a browser executable");
769
+ }
770
+ const direct = openWithInstalledBrowser(url, headless);
771
+ if (direct) return direct;
772
+ throw new Error(
773
+ headless
774
+ ? "could not find a headless-capable browser; set BROWSER to a Chromium/Firefox executable"
775
+ : "could not open a browser automatically; set BROWSER to a browser executable",
776
+ );
767
777
  }
768
778
  function openWithReusableBrowserWindow(url) {
769
- if (process.platform != "darwin")
770
- return null;
771
- if (!hasExecutable("osascript"))
772
- return null;
773
- const browserApp = resolveMacBrowserAppName(process.env.BROWSER?.trim() ?? "");
774
- if (!browserApp)
775
- return null;
776
- const script = buildMacBrowserOpenScript(browserApp, url);
777
- if (!script.length)
778
- return null;
779
- return spawn("osascript", script.flatMap((line) => ["-e", line]), { stdio: "ignore", detached: true });
779
+ if (process.platform != "darwin") return null;
780
+ if (!hasExecutable("osascript")) return null;
781
+ const browserApp = resolveMacBrowserAppName(
782
+ process.env.BROWSER?.trim() ?? "",
783
+ );
784
+ if (!browserApp) return null;
785
+ const script = buildMacBrowserOpenScript(browserApp, url);
786
+ if (!script.length) return null;
787
+ return spawn(
788
+ "osascript",
789
+ script.flatMap((line) => ["-e", line]),
790
+ { stdio: "ignore", detached: true },
791
+ );
780
792
  }
781
793
  function openWithSystemBrowser(url) {
782
- if (process.env.BROWSER) {
783
- return (spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null);
784
- }
785
- if (process.platform == "darwin") {
786
- if (!hasExecutable("open"))
787
- return null;
788
- return spawn("open", [url], { stdio: "ignore", detached: true });
789
- }
790
- if (process.platform == "win32") {
791
- if (!hasExecutable("cmd"))
792
- return null;
793
- return spawn("cmd", ["/c", "start", "", url], {
794
- stdio: "ignore",
795
- detached: true,
796
- });
797
- }
798
- if (!hasExecutable("xdg-open"))
799
- return null;
800
- return spawn("xdg-open", [url], { stdio: "ignore", detached: true });
794
+ if (process.env.BROWSER) {
795
+ return (
796
+ spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null
797
+ );
798
+ }
799
+ if (process.platform == "darwin") {
800
+ if (!hasExecutable("open")) return null;
801
+ return spawn("open", [url], { stdio: "ignore", detached: true });
802
+ }
803
+ if (process.platform == "win32") {
804
+ if (!hasExecutable("cmd")) return null;
805
+ return spawn("cmd", ["/c", "start", "", url], {
806
+ stdio: "ignore",
807
+ detached: true,
808
+ });
809
+ }
810
+ if (!hasExecutable("xdg-open")) return null;
811
+ return spawn("xdg-open", [url], { stdio: "ignore", detached: true });
801
812
  }
802
813
  function resolveMacBrowserAppName(browser) {
803
- const trimmed = browser.trim();
804
- if (!trimmed.length)
805
- return null;
806
- const extracted = extractMacAppNameFromExecutable(trimmed);
807
- if (extracted)
808
- return extracted;
809
- const command = splitCommand(trimmed)[0]?.toLowerCase() ?? "";
810
- if (!command.length)
811
- return null;
812
- const aliases = {
813
- chrome: "Google Chrome",
814
- "google-chrome": "Google Chrome",
815
- "google-chrome-stable": "Google Chrome",
816
- chromium: "Chromium",
817
- "chromium-browser": "Chromium",
818
- msedge: "Microsoft Edge",
819
- firefox: "Firefox",
820
- safari: "Safari",
821
- };
822
- return aliases[command] ?? null;
814
+ const trimmed = browser.trim();
815
+ if (!trimmed.length) return null;
816
+ const extracted = extractMacAppNameFromExecutable(trimmed);
817
+ if (extracted) return extracted;
818
+ const command = splitCommand(trimmed)[0]?.toLowerCase() ?? "";
819
+ if (!command.length) return null;
820
+ const aliases = {
821
+ chrome: "Google Chrome",
822
+ "google-chrome": "Google Chrome",
823
+ "google-chrome-stable": "Google Chrome",
824
+ chromium: "Chromium",
825
+ "chromium-browser": "Chromium",
826
+ msedge: "Microsoft Edge",
827
+ firefox: "Firefox",
828
+ safari: "Safari",
829
+ };
830
+ return aliases[command] ?? null;
823
831
  }
824
832
  function extractMacAppNameFromExecutable(browser) {
825
- const appMatch = browser.match(/\/([^/]+)\.app\/Contents\/MacOS\//);
826
- if (!appMatch?.[1])
827
- return null;
828
- return appMatch[1];
833
+ const appMatch = browser.match(/\/([^/]+)\.app\/Contents\/MacOS\//);
834
+ if (!appMatch?.[1]) return null;
835
+ return appMatch[1];
829
836
  }
830
837
  function buildMacBrowserOpenScript(appName, url) {
831
- const escapedApp = escapeAppleScriptString(appName);
832
- const escapedUrl = escapeAppleScriptString(url);
833
- const lower = appName.toLowerCase();
834
- if (lower.includes("chrome") ||
835
- lower.includes("chromium") ||
836
- lower.includes("edge")) {
837
- return [
838
- `tell application "${escapedApp}"`,
839
- "activate",
840
- "if (count of windows) = 0 then make new window",
841
- `set URL of active tab of front window to "${escapedUrl}"`,
842
- "end tell",
843
- ];
844
- }
845
- if (lower.includes("safari")) {
846
- return [
847
- `tell application "${escapedApp}"`,
848
- "activate",
849
- "if (count of windows) = 0 then make new document",
850
- `set URL of front document to "${escapedUrl}"`,
851
- "end tell",
852
- ];
853
- }
854
- if (lower.includes("firefox")) {
855
- return [
856
- `tell application "${escapedApp}"`,
857
- "activate",
858
- `open location "${escapedUrl}"`,
859
- "end tell",
860
- ];
861
- }
862
- return [];
838
+ const escapedApp = escapeAppleScriptString(appName);
839
+ const escapedUrl = escapeAppleScriptString(url);
840
+ const lower = appName.toLowerCase();
841
+ if (
842
+ lower.includes("chrome") ||
843
+ lower.includes("chromium") ||
844
+ lower.includes("edge")
845
+ ) {
846
+ return [
847
+ `tell application "${escapedApp}"`,
848
+ "activate",
849
+ "if (count of windows) = 0 then make new window",
850
+ `set URL of active tab of front window to "${escapedUrl}"`,
851
+ "end tell",
852
+ ];
853
+ }
854
+ if (lower.includes("safari")) {
855
+ return [
856
+ `tell application "${escapedApp}"`,
857
+ "activate",
858
+ "if (count of windows) = 0 then make new document",
859
+ `set URL of front document to "${escapedUrl}"`,
860
+ "end tell",
861
+ ];
862
+ }
863
+ if (lower.includes("firefox")) {
864
+ return [
865
+ `tell application "${escapedApp}"`,
866
+ "activate",
867
+ `open location "${escapedUrl}"`,
868
+ "end tell",
869
+ ];
870
+ }
871
+ return [];
863
872
  }
864
873
  function escapeAppleScriptString(value) {
865
- return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
874
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
866
875
  }
867
876
  function openWithInstalledBrowser(url, headless) {
868
- const browserEnv = process.env.BROWSER;
869
- if (browserEnv) {
870
- return spawnBrowserCommand(browserEnv, url, headless);
871
- }
872
- const candidates = [
873
- { command: "chromium", headless: [...WEB_HEADLESS_FLAGS] },
874
- { command: "chromium-browser", headless: [...WEB_HEADLESS_FLAGS] },
875
- { command: "google-chrome", headless: [...WEB_HEADLESS_FLAGS] },
876
- { command: "google-chrome-stable", headless: [...WEB_HEADLESS_FLAGS] },
877
- { command: "chrome", headless: [...WEB_HEADLESS_FLAGS] },
878
- { command: "msedge", headless: [...WEB_HEADLESS_FLAGS] },
879
- { command: "firefox", headless: ["-headless"] },
880
- ];
881
- for (const candidate of candidates) {
882
- if (!hasExecutable(candidate.command))
883
- continue;
884
- return {
885
- process: spawn(candidate.command, [...(headless ? candidate.headless : []), url], {
886
- stdio: ["ignore", "ignore", "pipe"],
887
- detached: true,
888
- }),
889
- tempProfileDir: null,
890
- ownsProcess: true,
891
- };
892
- }
893
- const playwrightFallback = resolvePlaywrightBrowserExecutable("chromium") ??
894
- resolvePlaywrightBrowserExecutable("firefox") ??
895
- resolvePlaywrightBrowserExecutable("webkit");
896
- if (playwrightFallback) {
897
- return spawnBrowserCommand(playwrightFallback, url, headless);
898
- }
899
- return null;
877
+ const browserEnv = process.env.BROWSER;
878
+ if (browserEnv) {
879
+ return spawnBrowserCommand(browserEnv, url, headless);
880
+ }
881
+ const candidates = [
882
+ { command: "chromium", headless: [...WEB_HEADLESS_FLAGS] },
883
+ { command: "chromium-browser", headless: [...WEB_HEADLESS_FLAGS] },
884
+ { command: "google-chrome", headless: [...WEB_HEADLESS_FLAGS] },
885
+ { command: "google-chrome-stable", headless: [...WEB_HEADLESS_FLAGS] },
886
+ { command: "chrome", headless: [...WEB_HEADLESS_FLAGS] },
887
+ { command: "msedge", headless: [...WEB_HEADLESS_FLAGS] },
888
+ { command: "firefox", headless: ["-headless"] },
889
+ ];
890
+ for (const candidate of candidates) {
891
+ if (!hasExecutable(candidate.command)) continue;
892
+ return {
893
+ process: spawn(
894
+ candidate.command,
895
+ [...(headless ? candidate.headless : []), url],
896
+ {
897
+ stdio: ["ignore", "ignore", "pipe"],
898
+ detached: true,
899
+ },
900
+ ),
901
+ tempProfileDir: null,
902
+ ownsProcess: true,
903
+ };
904
+ }
905
+ const playwrightFallback =
906
+ resolvePlaywrightBrowserExecutable("chromium") ??
907
+ resolvePlaywrightBrowserExecutable("firefox") ??
908
+ resolvePlaywrightBrowserExecutable("webkit");
909
+ if (playwrightFallback) {
910
+ return spawnBrowserCommand(playwrightFallback, url, headless);
911
+ }
912
+ return null;
900
913
  }
901
914
  function spawnBrowserCommand(commandValue, url, headless) {
902
- const directCommand = unwrapQuotedPath(String(commandValue).trim());
903
- if (hasExecutable(directCommand)) {
904
- const resolvedHeadless = headless
905
- ? resolveHeadlessLaunch(directCommand, directCommand)
906
- : { flags: [], tempProfileDir: null };
907
- const args = [...resolvedHeadless.flags];
908
- args.push(url);
909
- return {
910
- process: spawn(directCommand, args, {
911
- stdio: ["ignore", "ignore", "pipe"],
912
- detached: true,
913
- }),
914
- tempProfileDir: resolvedHeadless.tempProfileDir,
915
- ownsProcess: true,
916
- };
917
- }
918
- const parts = splitCommand(String(commandValue));
919
- if (!parts.length)
920
- return null;
921
- const command = parts[0];
922
- if (!hasExecutable(command))
923
- return null;
924
- const args = parts.slice(1);
925
- let tempProfileDir = null;
926
- if (headless) {
927
- const resolvedHeadless = resolveHeadlessLaunch(commandValue, command);
928
- args.push(...resolvedHeadless.flags);
929
- tempProfileDir = resolvedHeadless.tempProfileDir;
930
- }
915
+ const directCommand = unwrapQuotedPath(String(commandValue).trim());
916
+ if (hasExecutable(directCommand)) {
917
+ const resolvedHeadless = headless
918
+ ? resolveHeadlessLaunch(directCommand, directCommand)
919
+ : { flags: [], tempProfileDir: null };
920
+ const args = [...resolvedHeadless.flags];
931
921
  args.push(url);
932
922
  return {
933
- process: spawn(command, args, {
934
- stdio: ["ignore", "ignore", "pipe"],
935
- detached: true,
936
- }),
937
- tempProfileDir,
938
- ownsProcess: true,
923
+ process: spawn(directCommand, args, {
924
+ stdio: ["ignore", "ignore", "pipe"],
925
+ detached: true,
926
+ }),
927
+ tempProfileDir: resolvedHeadless.tempProfileDir,
928
+ ownsProcess: true,
939
929
  };
930
+ }
931
+ const parts = splitCommand(String(commandValue));
932
+ if (!parts.length) return null;
933
+ const command = parts[0];
934
+ if (!hasExecutable(command)) return null;
935
+ const args = parts.slice(1);
936
+ let tempProfileDir = null;
937
+ if (headless) {
938
+ const resolvedHeadless = resolveHeadlessLaunch(commandValue, command);
939
+ args.push(...resolvedHeadless.flags);
940
+ tempProfileDir = resolvedHeadless.tempProfileDir;
941
+ }
942
+ args.push(url);
943
+ return {
944
+ process: spawn(command, args, {
945
+ stdio: ["ignore", "ignore", "pipe"],
946
+ detached: true,
947
+ }),
948
+ tempProfileDir,
949
+ ownsProcess: true,
950
+ };
940
951
  }
941
952
  function splitCommand(commandValue) {
942
- const parts = [];
943
- let current = "";
944
- let quote = "";
945
- for (let i = 0; i < commandValue.length; i++) {
946
- const char = commandValue[i];
947
- if (quote) {
948
- if (char == quote) {
949
- quote = "";
950
- }
951
- else if (char == "\\" && i + 1 < commandValue.length) {
952
- current += commandValue[++i];
953
- }
954
- else {
955
- current += char;
956
- }
957
- continue;
958
- }
959
- if (char == "'" || char == '"') {
960
- quote = char;
961
- continue;
962
- }
963
- if (/\s/.test(char)) {
964
- if (current.length) {
965
- parts.push(current);
966
- current = "";
967
- }
968
- continue;
969
- }
970
- if (char == "\\" && i + 1 < commandValue.length) {
971
- current += commandValue[++i];
972
- continue;
973
- }
953
+ const parts = [];
954
+ let current = "";
955
+ let quote = "";
956
+ for (let i = 0; i < commandValue.length; i++) {
957
+ const char = commandValue[i];
958
+ if (quote) {
959
+ if (char == quote) {
960
+ quote = "";
961
+ } else if (char == "\\" && i + 1 < commandValue.length) {
962
+ current += commandValue[++i];
963
+ } else {
974
964
  current += char;
965
+ }
966
+ continue;
967
+ }
968
+ if (char == "'" || char == '"') {
969
+ quote = char;
970
+ continue;
975
971
  }
976
- if (current.length) {
972
+ if (/\s/.test(char)) {
973
+ if (current.length) {
977
974
  parts.push(current);
975
+ current = "";
976
+ }
977
+ continue;
978
+ }
979
+ if (char == "\\" && i + 1 < commandValue.length) {
980
+ current += commandValue[++i];
981
+ continue;
978
982
  }
979
- return parts;
983
+ current += char;
984
+ }
985
+ if (current.length) {
986
+ parts.push(current);
987
+ }
988
+ return parts;
980
989
  }
981
990
  function appendBrowserOutput(current, next) {
982
- const combined = current + next;
983
- if (combined.length <= 16384)
984
- return combined;
985
- return combined.slice(combined.length - 16384);
991
+ const combined = current + next;
992
+ if (combined.length <= 16384) return combined;
993
+ return combined.slice(combined.length - 16384);
986
994
  }
987
995
  function formatBrowserExitError(code, stderr) {
988
- const trimmed = stderr.trim();
989
- if (!trimmed.length) {
990
- return `web browser process exited with code ${code}`;
991
- }
992
- return `web browser process exited with code ${code}\nstderr:\n${trimmed}`;
996
+ const trimmed = stderr.trim();
997
+ if (!trimmed.length) {
998
+ return `web browser process exited with code ${code}`;
999
+ }
1000
+ return `web browser process exited with code ${code}\nstderr:\n${trimmed}`;
993
1001
  }
994
1002
  function killOwnedBrowserProcess(browserProcess) {
995
- try {
996
- if (process.platform != "win32" &&
997
- typeof browserProcess.pid == "number" &&
998
- browserProcess.pid > 0) {
999
- process.kill(-browserProcess.pid, "SIGTERM");
1000
- return;
1001
- }
1003
+ try {
1004
+ if (
1005
+ process.platform != "win32" &&
1006
+ typeof browserProcess.pid == "number" &&
1007
+ browserProcess.pid > 0
1008
+ ) {
1009
+ process.kill(-browserProcess.pid, "SIGTERM");
1010
+ return;
1002
1011
  }
1003
- catch { }
1004
- try {
1005
- browserProcess.kill("SIGTERM");
1006
- }
1007
- catch { }
1012
+ } catch {}
1013
+ try {
1014
+ browserProcess.kill("SIGTERM");
1015
+ } catch {}
1008
1016
  }
1009
1017
  function unwrapQuotedPath(value) {
1010
- if ((value.startsWith('"') && value.endsWith('"')) ||
1011
- (value.startsWith("'") && value.endsWith("'"))) {
1012
- return value.slice(1, -1);
1013
- }
1014
- return value;
1018
+ if (
1019
+ (value.startsWith('"') && value.endsWith('"')) ||
1020
+ (value.startsWith("'") && value.endsWith("'"))
1021
+ ) {
1022
+ return value.slice(1, -1);
1023
+ }
1024
+ return value;
1015
1025
  }
1016
1026
  function resolveHeadlessLaunch(commandValue, command) {
1017
- const lower = `${commandValue} ${command}`.toLowerCase();
1018
- if (lower.includes("firefox")) {
1019
- const tempProfileDir = fs.mkdtempSync(path.join(os.tmpdir(), "as-test-firefox-profile-"));
1020
- return {
1021
- flags: ["-headless", "-no-remote", "-profile", tempProfileDir],
1022
- tempProfileDir,
1023
- };
1024
- }
1025
- if (lower.includes("webkit") || lower.includes("minibrowser")) {
1026
- return { flags: ["--headless"], tempProfileDir: null };
1027
- }
1028
- return { flags: [...WEB_HEADLESS_FLAGS], tempProfileDir: null };
1027
+ const lower = `${commandValue} ${command}`.toLowerCase();
1028
+ if (lower.includes("firefox")) {
1029
+ const tempProfileDir = fs.mkdtempSync(
1030
+ path.join(os.tmpdir(), "as-test-firefox-profile-"),
1031
+ );
1032
+ return {
1033
+ flags: ["-headless", "-no-remote", "-profile", tempProfileDir],
1034
+ tempProfileDir,
1035
+ };
1036
+ }
1037
+ if (lower.includes("webkit") || lower.includes("minibrowser")) {
1038
+ return { flags: ["--headless"], tempProfileDir: null };
1039
+ }
1040
+ return { flags: [...WEB_HEADLESS_FLAGS], tempProfileDir: null };
1029
1041
  }
1030
1042
  function hasExecutable(command) {
1031
- if (!command.length)
1032
- return false;
1033
- if (command.includes("/") || command.includes("\\")) {
1034
- return fs.existsSync(command);
1043
+ if (!command.length) return false;
1044
+ if (command.includes("/") || command.includes("\\")) {
1045
+ return fs.existsSync(command);
1046
+ }
1047
+ const pathValue = process.env.PATH ?? "";
1048
+ const suffixes =
1049
+ process.platform == "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
1050
+ for (const base of pathValue.split(path.delimiter)) {
1051
+ if (!base) continue;
1052
+ for (const suffix of suffixes) {
1053
+ if (fs.existsSync(path.join(base, command + suffix))) {
1054
+ return true;
1055
+ }
1035
1056
  }
1036
- const pathValue = process.env.PATH ?? "";
1037
- const suffixes = process.platform == "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
1038
- for (const base of pathValue.split(path.delimiter)) {
1039
- if (!base)
1040
- continue;
1041
- for (const suffix of suffixes) {
1042
- if (fs.existsSync(path.join(base, command + suffix))) {
1043
- return true;
1044
- }
1045
- }
1046
- }
1047
- return false;
1057
+ }
1058
+ return false;
1048
1059
  }
1049
1060
  function resolvePlaywrightBrowserExecutable(browser) {
1050
- const patterns = getPlaywrightBrowserPatterns(browser);
1051
- if (!patterns.length)
1052
- return null;
1053
- for (const cacheRoot of getPlaywrightCacheRoots()) {
1054
- if (!fs.existsSync(cacheRoot))
1055
- continue;
1056
- for (const pattern of patterns) {
1057
- const matches = fs.globSync(path.join(cacheRoot, pattern)).sort();
1058
- if (matches.length) {
1059
- return matches[matches.length - 1];
1060
- }
1061
- }
1061
+ const patterns = getPlaywrightBrowserPatterns(browser);
1062
+ if (!patterns.length) return null;
1063
+ for (const cacheRoot of getPlaywrightCacheRoots()) {
1064
+ if (!fs.existsSync(cacheRoot)) continue;
1065
+ for (const pattern of patterns) {
1066
+ const matches = fs.globSync(path.join(cacheRoot, pattern)).sort();
1067
+ if (matches.length) {
1068
+ return matches[matches.length - 1];
1069
+ }
1062
1070
  }
1063
- return null;
1071
+ }
1072
+ return null;
1064
1073
  }
1065
1074
  function getPlaywrightCacheRoots() {
1066
- const roots = new Set();
1067
- const configured = process.env.PLAYWRIGHT_BROWSERS_PATH?.trim() ?? "";
1068
- if (configured.length && configured != "0") {
1069
- roots.add(path.resolve(configured));
1075
+ const roots = new Set();
1076
+ const configured = process.env.PLAYWRIGHT_BROWSERS_PATH?.trim() ?? "";
1077
+ if (configured.length && configured != "0") {
1078
+ roots.add(path.resolve(configured));
1079
+ }
1080
+ const home = process.env.HOME ?? "";
1081
+ if (process.platform == "darwin" && home.length) {
1082
+ roots.add(path.join(home, "Library", "Caches", "ms-playwright"));
1083
+ } else if (process.platform == "win32") {
1084
+ const localAppData = process.env.LOCALAPPDATA?.trim() ?? "";
1085
+ if (localAppData.length) {
1086
+ roots.add(path.join(localAppData, "ms-playwright"));
1070
1087
  }
1071
- const home = process.env.HOME ?? "";
1072
- if (process.platform == "darwin" && home.length) {
1073
- roots.add(path.join(home, "Library", "Caches", "ms-playwright"));
1088
+ const userProfile = process.env.USERPROFILE?.trim() ?? "";
1089
+ if (userProfile.length) {
1090
+ roots.add(path.join(userProfile, "AppData", "Local", "ms-playwright"));
1074
1091
  }
1075
- else if (process.platform == "win32") {
1076
- const localAppData = process.env.LOCALAPPDATA?.trim() ?? "";
1077
- if (localAppData.length) {
1078
- roots.add(path.join(localAppData, "ms-playwright"));
1079
- }
1080
- const userProfile = process.env.USERPROFILE?.trim() ?? "";
1081
- if (userProfile.length) {
1082
- roots.add(path.join(userProfile, "AppData", "Local", "ms-playwright"));
1083
- }
1084
- }
1085
- else if (home.length) {
1086
- roots.add(path.join(home, ".cache", "ms-playwright"));
1087
- }
1088
- return [...roots];
1092
+ } else if (home.length) {
1093
+ roots.add(path.join(home, ".cache", "ms-playwright"));
1094
+ }
1095
+ return [...roots];
1089
1096
  }
1090
1097
  function getPlaywrightBrowserPatterns(browser) {
1091
- if (process.platform == "darwin") {
1092
- const macMap = {
1093
- chromium: [
1094
- "chromium-*/chrome-mac*/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
1095
- "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
1096
- "chromium_headless_shell-*/chrome-headless-shell-mac*/chrome-headless-shell",
1097
- ],
1098
- chrome: [
1099
- "chromium-*/chrome-mac*/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
1100
- "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
1101
- "chromium_headless_shell-*/chrome-headless-shell-mac*/chrome-headless-shell",
1102
- ],
1103
- firefox: [
1104
- "firefox-*/firefox/*.app/Contents/MacOS/firefox",
1105
- "firefox-*/*.app/Contents/MacOS/firefox",
1106
- "firefox-*/firefox/firefox",
1107
- ],
1108
- webkit: ["webkit-*/pw_run.sh"],
1109
- };
1110
- return macMap[browser] ?? [];
1111
- }
1112
- if (process.platform == "win32") {
1113
- const winMap = {
1114
- chromium: [
1115
- "chromium-*/chrome-win/chrome.exe",
1116
- "chromium-*/chrome-win64/chrome.exe",
1117
- "chromium_headless_shell-*/chrome-headless-shell-win64/chrome-headless-shell.exe",
1118
- ],
1119
- chrome: [
1120
- "chromium-*/chrome-win/chrome.exe",
1121
- "chromium-*/chrome-win64/chrome.exe",
1122
- "chromium_headless_shell-*/chrome-headless-shell-win64/chrome-headless-shell.exe",
1123
- ],
1124
- firefox: ["firefox-*/firefox/firefox.exe"],
1125
- webkit: ["webkit-*/Playwright.exe"],
1126
- };
1127
- return winMap[browser] ?? [];
1128
- }
1129
- const linuxMap = {
1130
- chromium: [
1131
- "chromium-*/chrome-linux/chrome",
1132
- "chromium-*/chrome-linux64/chrome",
1133
- "chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell",
1134
- ],
1135
- chrome: [
1136
- "chromium-*/chrome-linux/chrome",
1137
- "chromium-*/chrome-linux64/chrome",
1138
- "chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell",
1139
- ],
1140
- firefox: ["firefox-*/firefox/firefox"],
1141
- webkit: ["webkit-*/pw_run.sh"],
1098
+ if (process.platform == "darwin") {
1099
+ const macMap = {
1100
+ chromium: [
1101
+ "chromium-*/chrome-mac*/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
1102
+ "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
1103
+ "chromium_headless_shell-*/chrome-headless-shell-mac*/chrome-headless-shell",
1104
+ ],
1105
+ chrome: [
1106
+ "chromium-*/chrome-mac*/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing",
1107
+ "chromium-*/chrome-mac*/Chromium.app/Contents/MacOS/Chromium",
1108
+ "chromium_headless_shell-*/chrome-headless-shell-mac*/chrome-headless-shell",
1109
+ ],
1110
+ firefox: [
1111
+ "firefox-*/firefox/*.app/Contents/MacOS/firefox",
1112
+ "firefox-*/*.app/Contents/MacOS/firefox",
1113
+ "firefox-*/firefox/firefox",
1114
+ ],
1115
+ webkit: ["webkit-*/pw_run.sh"],
1142
1116
  };
1143
- return linuxMap[browser] ?? [];
1117
+ return macMap[browser] ?? [];
1118
+ }
1119
+ if (process.platform == "win32") {
1120
+ const winMap = {
1121
+ chromium: [
1122
+ "chromium-*/chrome-win/chrome.exe",
1123
+ "chromium-*/chrome-win64/chrome.exe",
1124
+ "chromium_headless_shell-*/chrome-headless-shell-win64/chrome-headless-shell.exe",
1125
+ ],
1126
+ chrome: [
1127
+ "chromium-*/chrome-win/chrome.exe",
1128
+ "chromium-*/chrome-win64/chrome.exe",
1129
+ "chromium_headless_shell-*/chrome-headless-shell-win64/chrome-headless-shell.exe",
1130
+ ],
1131
+ firefox: ["firefox-*/firefox/firefox.exe"],
1132
+ webkit: ["webkit-*/Playwright.exe"],
1133
+ };
1134
+ return winMap[browser] ?? [];
1135
+ }
1136
+ const linuxMap = {
1137
+ chromium: [
1138
+ "chromium-*/chrome-linux/chrome",
1139
+ "chromium-*/chrome-linux64/chrome",
1140
+ "chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell",
1141
+ ],
1142
+ chrome: [
1143
+ "chromium-*/chrome-linux/chrome",
1144
+ "chromium-*/chrome-linux64/chrome",
1145
+ "chromium_headless_shell-*/chrome-headless-shell-linux64/chrome-headless-shell",
1146
+ ],
1147
+ firefox: ["firefox-*/firefox/firefox"],
1148
+ webkit: ["webkit-*/pw_run.sh"],
1149
+ };
1150
+ return linuxMap[browser] ?? [];
1144
1151
  }
1145
1152
  async function captureHelperInstance(runHelper) {
1146
- const originalInstantiate = WebAssembly.instantiate.bind(WebAssembly);
1147
- let instance = null;
1148
- WebAssembly.instantiate = (async (source, importObject) => {
1149
- const result = await originalInstantiate(source, importObject);
1150
- if (result instanceof WebAssembly.Instance) {
1151
- instance = result;
1152
- }
1153
- return result;
1154
- });
1155
- try {
1156
- await runHelper();
1157
- }
1158
- finally {
1159
- WebAssembly.instantiate =
1160
- originalInstantiate;
1161
- }
1162
- if (!instance) {
1163
- throw new Error("bindings helper did not produce a WebAssembly.Instance");
1153
+ const originalInstantiate = WebAssembly.instantiate.bind(WebAssembly);
1154
+ let instance = null;
1155
+ WebAssembly.instantiate = async (source, importObject) => {
1156
+ const result = await originalInstantiate(source, importObject);
1157
+ if (result instanceof WebAssembly.Instance) {
1158
+ instance = result;
1164
1159
  }
1165
- return instance;
1160
+ return result;
1161
+ };
1162
+ try {
1163
+ await runHelper();
1164
+ } finally {
1165
+ WebAssembly.instantiate = originalInstantiate;
1166
+ }
1167
+ if (!instance) {
1168
+ throw new Error("bindings helper did not produce a WebAssembly.Instance");
1169
+ }
1170
+ return instance;
1166
1171
  }