as-test 1.1.1 → 1.1.3

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.
@@ -5,7 +5,6 @@ import http from "http";
5
5
  import os from "os";
6
6
  import path from "path";
7
7
  import { pathToFileURL } from "url";
8
- import { WASI } from "wasi";
9
8
  import { buildWebRunnerClientSource } from "./web-runner/client.js";
10
9
  import { buildWebRunnerHtml } from "./web-runner/html.js";
11
10
  import { buildWebRunnerWorkerSource } from "./web-runner/worker.js";
@@ -25,29 +24,81 @@ function withNodeIo(imports) {
25
24
  }
26
25
  export async function instantiate(imports) {
27
26
  validateImports(imports, "instantiate");
28
- const wasmPath = process.env.AS_TEST_WASM_PATH;
29
- if (!wasmPath || !wasmPath.length) {
30
- throw new Error("AS_TEST_WASM_PATH is not set; as-test must resolve the wasm artifact before launching the runner");
31
- }
32
- const target = (process.env.AS_TEST_RUNTIME_TARGET || "bindings");
27
+ const wasmPath = resolveWasmPath();
28
+ const target = resolveRuntimeTarget();
33
29
  if (target == "wasi") {
34
30
  return instantiateWasiInstance(wasmPath, imports);
35
31
  }
36
32
  if (target == "web") {
37
33
  return instantiateWebInstance(wasmPath, imports);
38
34
  }
39
- const kind = (process.env.AS_TEST_BINDINGS_KIND || "none");
35
+ const helperPath = resolveBindingsHelperPath(wasmPath);
36
+ const kind = resolveBindingsKind(helperPath);
40
37
  if (kind == "raw") {
41
- return instantiateRawInstance(wasmPath, imports);
38
+ return instantiateRawInstance(wasmPath, helperPath, imports);
42
39
  }
43
40
  if (kind == "esm") {
44
- return instantiateEsmInstance(wasmPath, imports);
41
+ return instantiateEsmInstance(wasmPath, helperPath, imports);
45
42
  }
46
43
  if (kind == "none") {
47
44
  return instantiateNoBindingsInstance(wasmPath, imports);
48
45
  }
49
46
  throw new Error(`unsupported bindings kind "${kind}"`);
50
47
  }
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";
59
+ }
60
+ 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"));
79
+ }
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 : "";
87
+ }
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";
101
+ }
51
102
  function validateImports(imports, fnName) {
52
103
  if (arguments.length < 1) {
53
104
  throw new Error(`${fnName}(imports) requires an imports object; pass {} when unused`);
@@ -135,9 +186,8 @@ function mergeImports(...groups) {
135
186
  }
136
187
  return out;
137
188
  }
138
- async function instantiateRawInstance(wasmPath, imports) {
189
+ async function instantiateRawInstance(wasmPath, helperPath, imports) {
139
190
  validateImports(imports, "instantiateRawInstance");
140
- const helperPath = process.env.AS_TEST_HELPER_PATH || "";
141
191
  if (!helperPath.length) {
142
192
  throw new Error("bindings kind is raw but AS_TEST_HELPER_PATH is not set");
143
193
  }
@@ -153,9 +203,8 @@ async function instantiateRawInstance(wasmPath, imports) {
153
203
  });
154
204
  return decorateInstance(instance, "bindings");
155
205
  }
156
- async function instantiateEsmInstance(wasmPath, imports) {
206
+ async function instantiateEsmInstance(wasmPath, helperPath, imports) {
157
207
  validateImports(imports, "instantiateEsmInstance");
158
- const helperPath = process.env.AS_TEST_HELPER_PATH || "";
159
208
  if (!helperPath.length) {
160
209
  throw new Error("bindings kind is esm but AS_TEST_HELPER_PATH is not set");
161
210
  }
@@ -175,6 +224,7 @@ async function instantiateNoBindingsInstance(wasmPath, imports) {
175
224
  async function instantiateWasiInstance(wasmPath, imports) {
176
225
  validateImports(imports, "instantiateWasiInstance");
177
226
  suppressExperimentalWasiWarning();
227
+ const { WASI } = await import("wasi");
178
228
  const binary = fs.readFileSync(wasmPath);
179
229
  const module = new WebAssembly.Module(binary);
180
230
  const wasi = new WASI({
@@ -194,7 +244,8 @@ async function instantiateWebInstance(wasmPath, imports) {
194
244
  if (hasUserImports(imports)) {
195
245
  throw new Error("web runtime does not support custom imports in the default runner; pass {} or write a custom web runner");
196
246
  }
197
- const bindingsKind = (process.env.AS_TEST_BINDINGS_KIND || "raw");
247
+ const bindingsKind = (process.env.AS_TEST_BINDINGS_KIND ||
248
+ "raw");
198
249
  const helperPath = process.env.AS_TEST_HELPER_PATH
199
250
  ? path.resolve(process.cwd(), process.env.AS_TEST_HELPER_PATH)
200
251
  : wasmPath.replace(/\.wasm$/, ".js");
@@ -420,9 +471,9 @@ async function instantiateWebInstance(wasmPath, imports) {
420
471
  ...headers,
421
472
  "Content-Type": "text/html; charset=utf-8",
422
473
  });
423
- res.end(html.replace("</body>", ' <script>window.__AS_TEST_ENV__ = ' +
474
+ res.end(html.replace("</body>", " <script>window.__AS_TEST_ENV__ = " +
424
475
  JSON.stringify(webRuntimeEnv) +
425
- ';</script>\n </body>'));
476
+ ";</script>\n </body>"));
426
477
  return;
427
478
  }
428
479
  if (url == "/client.js") {
@@ -500,9 +551,7 @@ async function instantiateWebInstance(wasmPath, imports) {
500
551
  });
501
552
  socket.on("error", (error) => {
502
553
  if (!finished) {
503
- rejectOnce(error instanceof Error
504
- ? error
505
- : new Error(String(error)));
554
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
506
555
  }
507
556
  });
508
557
  flushPendingFrames();
@@ -600,7 +649,7 @@ function suppressExperimentalWasiWarning() {
600
649
  const message = typeof warning == "string"
601
650
  ? warning
602
651
  : String(warning && typeof warning == "object" && "message" in warning
603
- ? warning.message ?? ""
652
+ ? (warning.message ?? "")
604
653
  : "");
605
654
  if (name == "ExperimentalWarning" &&
606
655
  message.includes("WASI is an experimental feature")) {
@@ -731,7 +780,7 @@ function openWithReusableBrowserWindow(url) {
731
780
  }
732
781
  function openWithSystemBrowser(url) {
733
782
  if (process.env.BROWSER) {
734
- return spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null;
783
+ return (spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null);
735
784
  }
736
785
  if (process.platform == "darwin") {
737
786
  if (!hasExecutable("open"))
@@ -788,7 +837,7 @@ function buildMacBrowserOpenScript(appName, url) {
788
837
  return [
789
838
  `tell application "${escapedApp}"`,
790
839
  "activate",
791
- 'if (count of windows) = 0 then make new window',
840
+ "if (count of windows) = 0 then make new window",
792
841
  `set URL of active tab of front window to "${escapedUrl}"`,
793
842
  "end tell",
794
843
  ];
@@ -797,7 +846,7 @@ function buildMacBrowserOpenScript(appName, url) {
797
846
  return [
798
847
  `tell application "${escapedApp}"`,
799
848
  "activate",
800
- 'if (count of windows) = 0 then make new document',
849
+ "if (count of windows) = 0 then make new document",
801
850
  `set URL of front document to "${escapedUrl}"`,
802
851
  "end tell",
803
852
  ];
@@ -1107,7 +1156,8 @@ async function captureHelperInstance(runHelper) {
1107
1156
  await runHelper();
1108
1157
  }
1109
1158
  finally {
1110
- WebAssembly.instantiate = originalInstantiate;
1159
+ WebAssembly.instantiate =
1160
+ originalInstantiate;
1111
1161
  }
1112
1162
  if (!instance) {
1113
1163
  throw new Error("bindings helper did not produce a WebAssembly.Instance");
package/lib/src/index.ts CHANGED
@@ -6,7 +6,7 @@ import os from "os";
6
6
  import path from "path";
7
7
  import type { Duplex } from "stream";
8
8
  import { pathToFileURL } from "url";
9
- import { WASI } from "wasi";
9
+ import type { WASI } from "wasi";
10
10
  import { buildWebRunnerClientSource } from "./web-runner/client.js";
11
11
  import { buildWebRunnerHtml } from "./web-runner/html.js";
12
12
  import { buildWebRunnerWorkerSource } from "./web-runner/worker.js";
@@ -45,26 +45,22 @@ export async function instantiate(
45
45
  ): Promise<WebAssembly.Instance> {
46
46
  validateImports(imports, "instantiate");
47
47
 
48
- const wasmPath = process.env.AS_TEST_WASM_PATH;
49
- if (!wasmPath || !wasmPath.length) {
50
- throw new Error(
51
- "AS_TEST_WASM_PATH is not set; as-test must resolve the wasm artifact before launching the runner",
52
- );
53
- }
54
- const target = (process.env.AS_TEST_RUNTIME_TARGET || "bindings") as RuntimeTarget;
48
+ const wasmPath = resolveWasmPath();
49
+ const target = resolveRuntimeTarget();
55
50
  if (target == "wasi") {
56
51
  return instantiateWasiInstance(wasmPath, imports);
57
52
  }
58
53
  if (target == "web") {
59
54
  return instantiateWebInstance(wasmPath, imports);
60
55
  }
61
- const kind = (process.env.AS_TEST_BINDINGS_KIND || "none") as BindingsKind;
56
+ const helperPath = resolveBindingsHelperPath(wasmPath);
57
+ const kind = resolveBindingsKind(helperPath);
62
58
 
63
59
  if (kind == "raw") {
64
- return instantiateRawInstance(wasmPath, imports);
60
+ return instantiateRawInstance(wasmPath, helperPath, imports);
65
61
  }
66
62
  if (kind == "esm") {
67
- return instantiateEsmInstance(wasmPath, imports);
63
+ return instantiateEsmInstance(wasmPath, helperPath, imports);
68
64
  }
69
65
  if (kind == "none") {
70
66
  return instantiateNoBindingsInstance(wasmPath, imports);
@@ -72,6 +68,64 @@ export async function instantiate(
72
68
  throw new Error(`unsupported bindings kind "${kind}"`);
73
69
  }
74
70
 
71
+ function resolveRuntimeTarget(): RuntimeTarget {
72
+ const envTarget = process.env.AS_TEST_RUNTIME_TARGET?.trim();
73
+ if (envTarget == "bindings" || envTarget == "wasi" || envTarget == "web") {
74
+ return envTarget;
75
+ }
76
+ const runnerPath = String(process.argv[1] ?? "");
77
+ if (runnerPath.includes(".wasi.")) return "wasi";
78
+ if (runnerPath.includes(".web.")) return "web";
79
+ return "bindings";
80
+ }
81
+
82
+ function resolveWasmPath(): string {
83
+ const envWasmPath = process.env.AS_TEST_WASM_PATH?.trim();
84
+ if (envWasmPath?.length) {
85
+ return path.resolve(envWasmPath);
86
+ }
87
+ const argWasmPath = process.argv[2]?.trim();
88
+ if (argWasmPath?.length) {
89
+ return path.resolve(argWasmPath);
90
+ }
91
+ const runnerPath = String(process.argv[1] ?? "");
92
+ const runnerName = path.basename(runnerPath || "runner.js");
93
+ throw new Error(
94
+ [
95
+ "No wasm artifact was provided for this runner.",
96
+ "",
97
+ `Direct usage: node .as-test/runners/${runnerName} .as-test/build/<artifact>.wasm`,
98
+ "Managed usage: bunx ast test --mode <mode>",
99
+ "",
100
+ "as-test normally sets AS_TEST_WASM_PATH automatically when it launches the runner.",
101
+ ].join("\n"),
102
+ );
103
+ }
104
+
105
+ function resolveBindingsHelperPath(wasmPath: string): string {
106
+ const envHelperPath = process.env.AS_TEST_HELPER_PATH?.trim();
107
+ if (envHelperPath?.length) {
108
+ return path.resolve(envHelperPath);
109
+ }
110
+ const candidate = wasmPath.replace(/\.wasm$/i, ".js");
111
+ return fs.existsSync(candidate) ? candidate : "";
112
+ }
113
+
114
+ function resolveBindingsKind(helperPath: string): BindingsKind {
115
+ const envKind = process.env.AS_TEST_BINDINGS_KIND?.trim();
116
+ if (envKind == "raw" || envKind == "esm" || envKind == "none") {
117
+ return envKind;
118
+ }
119
+ if (!helperPath.length) {
120
+ return "none";
121
+ }
122
+ const source = fs.readFileSync(helperPath, "utf8");
123
+ if (/\bexport\s+(?:async\s+)?function\s+instantiate\b/.test(source)) {
124
+ return "raw";
125
+ }
126
+ return "esm";
127
+ }
128
+
75
129
  function validateImports(
76
130
  imports: WebAssembly.Imports,
77
131
  fnName: string,
@@ -173,16 +227,18 @@ function mergeImports(...groups: unknown[]): AnyImports {
173
227
 
174
228
  async function instantiateRawInstance(
175
229
  wasmPath: string,
230
+ helperPath: string,
176
231
  imports: WebAssembly.Imports,
177
232
  ): Promise<WebAssembly.Instance> {
178
233
  validateImports(imports, "instantiateRawInstance");
179
- const helperPath = process.env.AS_TEST_HELPER_PATH || "";
180
234
  if (!helperPath.length) {
181
235
  throw new Error("bindings kind is raw but AS_TEST_HELPER_PATH is not set");
182
236
  }
183
237
  const binary = fs.readFileSync(wasmPath);
184
238
  const module = new WebAssembly.Module(binary);
185
- const helper = (await import(`${pathToFileURL(helperPath).href}?t=${Date.now()}`)) as {
239
+ const helper = (await import(
240
+ `${pathToFileURL(helperPath).href}?t=${Date.now()}`
241
+ )) as {
186
242
  instantiate?: (
187
243
  module: WebAssembly.Module,
188
244
  imports?: WebAssembly.Imports,
@@ -200,10 +256,10 @@ async function instantiateRawInstance(
200
256
 
201
257
  async function instantiateEsmInstance(
202
258
  wasmPath: string,
259
+ helperPath: string,
203
260
  imports: WebAssembly.Imports,
204
261
  ): Promise<WebAssembly.Instance> {
205
262
  validateImports(imports, "instantiateEsmInstance");
206
- const helperPath = process.env.AS_TEST_HELPER_PATH || "";
207
263
  if (!helperPath.length) {
208
264
  throw new Error("bindings kind is esm but AS_TEST_HELPER_PATH is not set");
209
265
  }
@@ -233,6 +289,7 @@ async function instantiateWasiInstance(
233
289
  ): Promise<WebAssembly.Instance> {
234
290
  validateImports(imports, "instantiateWasiInstance");
235
291
  suppressExperimentalWasiWarning();
292
+ const { WASI } = await import("wasi");
236
293
  const binary = fs.readFileSync(wasmPath);
237
294
  const module = new WebAssembly.Module(binary);
238
295
  const wasi = new WASI({
@@ -259,7 +316,8 @@ async function instantiateWebInstance(
259
316
  );
260
317
  }
261
318
 
262
- const bindingsKind = (process.env.AS_TEST_BINDINGS_KIND || "raw") as BindingsKind;
319
+ const bindingsKind = (process.env.AS_TEST_BINDINGS_KIND ||
320
+ "raw") as BindingsKind;
263
321
  const helperPath = process.env.AS_TEST_HELPER_PATH
264
322
  ? path.resolve(process.cwd(), process.env.AS_TEST_HELPER_PATH)
265
323
  : wasmPath.replace(/\.wasm$/, ".js");
@@ -378,9 +436,11 @@ async function instantiateWebInstance(
378
436
  if (message?.kind == "instantiated") {
379
437
  if (resolved) return;
380
438
  resolved = true;
381
- resolve(createWebInstanceController(() => {
382
- sendControl({ kind: "start" });
383
- }));
439
+ resolve(
440
+ createWebInstanceController(() => {
441
+ sendControl({ kind: "start" });
442
+ }),
443
+ );
384
444
  return;
385
445
  }
386
446
  if (message?.kind == "done") {
@@ -477,9 +537,9 @@ async function instantiateWebInstance(
477
537
  res.end(
478
538
  html.replace(
479
539
  "</body>",
480
- ' <script>window.__AS_TEST_ENV__ = ' +
540
+ " <script>window.__AS_TEST_ENV__ = " +
481
541
  JSON.stringify(webRuntimeEnv) +
482
- ';</script>\n </body>',
542
+ ";</script>\n </body>",
483
543
  ),
484
544
  );
485
545
  return;
@@ -561,11 +621,7 @@ async function instantiateWebInstance(
561
621
  });
562
622
  socket.on("error", (error) => {
563
623
  if (!finished) {
564
- rejectOnce(
565
- error instanceof Error
566
- ? error
567
- : new Error(String(error)),
568
- );
624
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
569
625
  }
570
626
  });
571
627
  flushPendingFrames();
@@ -622,7 +678,9 @@ async function instantiateWebInstance(
622
678
  return;
623
679
  }
624
680
  if (code && code != 0) {
625
- rejectOnce(new Error(formatBrowserExitError(code, browserStderr)));
681
+ rejectOnce(
682
+ new Error(formatBrowserExitError(code, browserStderr)),
683
+ );
626
684
  return;
627
685
  }
628
686
  if (ready || wsSocket) {
@@ -631,9 +689,7 @@ async function instantiateWebInstance(
631
689
  });
632
690
  }
633
691
  } catch (error) {
634
- rejectOnce(
635
- error instanceof Error ? error : new Error(String(error)),
636
- );
692
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
637
693
  }
638
694
  });
639
695
  });
@@ -680,7 +736,7 @@ function suppressExperimentalWasiWarning(): void {
680
736
  ? warning
681
737
  : String(
682
738
  warning && typeof warning == "object" && "message" in warning
683
- ? (warning as { message?: unknown }).message ?? ""
739
+ ? ((warning as { message?: unknown }).message ?? "")
684
740
  : "",
685
741
  );
686
742
  if (
@@ -831,7 +887,9 @@ function launchWebBrowser(
831
887
  function openWithReusableBrowserWindow(url: string): ChildProcess | null {
832
888
  if (process.platform != "darwin") return null;
833
889
  if (!hasExecutable("osascript")) return null;
834
- const browserApp = resolveMacBrowserAppName(process.env.BROWSER?.trim() ?? "");
890
+ const browserApp = resolveMacBrowserAppName(
891
+ process.env.BROWSER?.trim() ?? "",
892
+ );
835
893
  if (!browserApp) return null;
836
894
  const script = buildMacBrowserOpenScript(browserApp, url);
837
895
  if (!script.length) return null;
@@ -844,7 +902,9 @@ function openWithReusableBrowserWindow(url: string): ChildProcess | null {
844
902
 
845
903
  function openWithSystemBrowser(url: string): ChildProcess | null {
846
904
  if (process.env.BROWSER) {
847
- return spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null;
905
+ return (
906
+ spawnBrowserCommand(process.env.BROWSER, url, false)?.process ?? null
907
+ );
848
908
  }
849
909
  if (process.platform == "darwin") {
850
910
  if (!hasExecutable("open")) return null;
@@ -901,7 +961,7 @@ function buildMacBrowserOpenScript(appName: string, url: string): string[] {
901
961
  return [
902
962
  `tell application "${escapedApp}"`,
903
963
  "activate",
904
- 'if (count of windows) = 0 then make new window',
964
+ "if (count of windows) = 0 then make new window",
905
965
  `set URL of active tab of front window to "${escapedUrl}"`,
906
966
  "end tell",
907
967
  ];
@@ -910,7 +970,7 @@ function buildMacBrowserOpenScript(appName: string, url: string): string[] {
910
970
  return [
911
971
  `tell application "${escapedApp}"`,
912
972
  "activate",
913
- 'if (count of windows) = 0 then make new document',
973
+ "if (count of windows) = 0 then make new document",
914
974
  `set URL of front document to "${escapedUrl}"`,
915
975
  "end tell",
916
976
  ];
@@ -1256,7 +1316,8 @@ async function captureHelperInstance(
1256
1316
  try {
1257
1317
  await runHelper();
1258
1318
  } finally {
1259
- WebAssembly.instantiate = originalInstantiate as typeof WebAssembly.instantiate;
1319
+ WebAssembly.instantiate =
1320
+ originalInstantiate as typeof WebAssembly.instantiate;
1260
1321
  }
1261
1322
 
1262
1323
  if (!instance) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "as-test",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "author": "Jairus Tanaka",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,6 +20,7 @@
20
20
  "as-test": "./",
21
21
  "assemblyscript": "^0.28.17",
22
22
  "assemblyscript-prettier": "^3.0.4",
23
+ "json-as": "^1.3.5",
23
24
  "prettier": "3.8.3",
24
25
  "try-as": "^1.0.1",
25
26
  "typescript": "^6.0.3",