@nimbus-sh/worker 0.12.0 → 0.13.0

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 (198) hide show
  1. package/dist/esbuild-cli-artifact.generated.d.ts +1 -1
  2. package/dist/esbuild-cli-artifact.generated.js +4 -4
  3. package/dist/esbuild-wasm-bundle.generated.d.ts +11 -10
  4. package/dist/esbuild-wasm-bundle.generated.d.ts.map +1 -1
  5. package/dist/esbuild-wasm-bundle.generated.js +11 -10
  6. package/dist/facets/build-facet.d.ts +53 -0
  7. package/dist/facets/build-facet.d.ts.map +1 -0
  8. package/dist/facets/build-facet.js +346 -0
  9. package/dist/facets/cirrus-real.d.ts +3 -0
  10. package/dist/facets/cirrus-real.d.ts.map +1 -1
  11. package/dist/facets/cirrus-real.js +1 -2
  12. package/dist/facets/compose.d.ts +1 -0
  13. package/dist/facets/compose.d.ts.map +1 -1
  14. package/dist/facets/compose.js +3 -0
  15. package/dist/facets/data-plan.d.ts +14 -30
  16. package/dist/facets/data-plan.d.ts.map +1 -1
  17. package/dist/facets/data-plan.js +153 -13
  18. package/dist/facets/dev-stylesheet.d.ts +27 -0
  19. package/dist/facets/dev-stylesheet.d.ts.map +1 -0
  20. package/dist/facets/dev-stylesheet.js +65 -0
  21. package/dist/facets/esbuild-transform.d.ts +23 -18
  22. package/dist/facets/esbuild-transform.d.ts.map +1 -1
  23. package/dist/facets/esbuild-transform.js +63 -109
  24. package/dist/facets/manager.d.ts +49 -9
  25. package/dist/facets/manager.d.ts.map +1 -1
  26. package/dist/facets/manager.js +238 -69
  27. package/dist/facets/oxc-transform.d.ts +29 -0
  28. package/dist/facets/oxc-transform.d.ts.map +1 -0
  29. package/dist/facets/oxc-transform.js +198 -0
  30. package/dist/facets/prebundle-pool.d.ts +23 -0
  31. package/dist/facets/prebundle-pool.d.ts.map +1 -0
  32. package/dist/facets/prebundle-pool.js +55 -0
  33. package/dist/facets/process.d.ts +13 -6
  34. package/dist/facets/process.d.ts.map +1 -1
  35. package/dist/facets/process.js +37 -31
  36. package/dist/facets/transform-store.d.ts +4 -4
  37. package/dist/facets/transform-store.js +4 -4
  38. package/dist/facets/vite-dev-server.d.ts +89 -12
  39. package/dist/facets/vite-dev-server.d.ts.map +1 -1
  40. package/dist/facets/vite-dev-server.js +66 -43
  41. package/dist/hosted/commands.d.ts.map +1 -1
  42. package/dist/hosted/commands.js +20 -4
  43. package/dist/hosted/runtime.d.ts.map +1 -1
  44. package/dist/hosted/runtime.js +7 -2
  45. package/dist/hosted/services.d.ts +3 -3
  46. package/dist/hosted/services.d.ts.map +1 -1
  47. package/dist/hosted/services.js +102 -95
  48. package/dist/hosted/session.d.ts +1 -0
  49. package/dist/hosted/session.d.ts.map +1 -1
  50. package/dist/loaders/generated-workers.d.ts +3 -0
  51. package/dist/loaders/generated-workers.d.ts.map +1 -1
  52. package/dist/loaders/generated-workers.js +3 -0
  53. package/dist/napi-wasm-artifacts.generated.js +2 -2
  54. package/dist/node-shims-artifact.generated.d.ts +14 -1
  55. package/dist/node-shims-artifact.generated.d.ts.map +1 -1
  56. package/dist/node-shims-artifact.generated.js +23 -10
  57. package/dist/npm/bin-links.d.ts +7 -4
  58. package/dist/npm/bin-links.d.ts.map +1 -1
  59. package/dist/npm/bin-links.js +14 -55
  60. package/dist/npm/cache.d.ts +6 -0
  61. package/dist/npm/cache.d.ts.map +1 -1
  62. package/dist/npm/cache.js +26 -4
  63. package/dist/npm/installer.d.ts +1 -1
  64. package/dist/npm/installer.d.ts.map +1 -1
  65. package/dist/npm/installer.js +26 -52
  66. package/dist/npm/pre-bundle-facet.d.ts +15 -117
  67. package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
  68. package/dist/npm/pre-bundle-facet.js +13 -511
  69. package/dist/oxc-facet-artifact.generated.d.ts +18 -0
  70. package/dist/oxc-facet-artifact.generated.d.ts.map +1 -0
  71. package/dist/oxc-facet-artifact.generated.js +17 -0
  72. package/dist/oxc-wasm-artifact.generated.d.ts +20 -0
  73. package/dist/oxc-wasm-artifact.generated.d.ts.map +1 -0
  74. package/dist/oxc-wasm-artifact.generated.js +19 -0
  75. package/dist/rolldown-facet-artifact.generated.d.ts +18 -0
  76. package/dist/rolldown-facet-artifact.generated.d.ts.map +1 -0
  77. package/dist/rolldown-facet-artifact.generated.js +17 -0
  78. package/dist/router/remote-api.d.ts.map +1 -1
  79. package/dist/router/remote-api.js +9 -0
  80. package/dist/runtime/bash-repl.d.ts.map +1 -1
  81. package/dist/runtime/bash-repl.js +1 -4
  82. package/dist/runtime/cpython-resident.d.ts.map +1 -1
  83. package/dist/runtime/cpython-resident.js +1 -0
  84. package/dist/runtime/esbuild-wasm-bytes.d.ts +25 -23
  85. package/dist/runtime/esbuild-wasm-bytes.d.ts.map +1 -1
  86. package/dist/runtime/esbuild-wasm-bytes.js +30 -25
  87. package/dist/runtime/facet-loader-host.d.ts +5 -4
  88. package/dist/runtime/facet-loader-host.d.ts.map +1 -1
  89. package/dist/runtime/facet-loader-host.js +5 -4
  90. package/dist/runtime/node-runner.d.ts +2 -0
  91. package/dist/runtime/node-runner.d.ts.map +1 -1
  92. package/dist/runtime/node-runner.js +1 -0
  93. package/dist/runtime/node-shims-artifact.d.ts +8 -1
  94. package/dist/runtime/node-shims-artifact.d.ts.map +1 -1
  95. package/dist/runtime/node-shims-artifact.js +32 -4
  96. package/dist/runtime/node-shims.d.ts.map +1 -1
  97. package/dist/runtime/node-shims.js +145 -109
  98. package/dist/runtime/opencode-facet-runner.d.ts.map +1 -1
  99. package/dist/runtime/opencode-facet-runner.js +6 -2
  100. package/dist/runtime/oxc-wasm-bytes.d.ts +19 -0
  101. package/dist/runtime/oxc-wasm-bytes.d.ts.map +1 -0
  102. package/dist/runtime/oxc-wasm-bytes.js +39 -0
  103. package/dist/runtime/package-manager.js +1 -1
  104. package/dist/runtime/process-logs-api.d.ts.map +1 -1
  105. package/dist/runtime/process-logs-api.js +2 -0
  106. package/dist/runtime/project-fs.d.ts +8 -1
  107. package/dist/runtime/project-fs.d.ts.map +1 -1
  108. package/dist/runtime/project-fs.js +15 -1
  109. package/dist/runtime/python-repl.d.ts +40 -1
  110. package/dist/runtime/python-repl.d.ts.map +1 -1
  111. package/dist/runtime/python-repl.js +19 -11
  112. package/dist/runtime/ruby-repl.d.ts +38 -0
  113. package/dist/runtime/ruby-repl.d.ts.map +1 -1
  114. package/dist/runtime/ruby-repl.js +24 -5
  115. package/dist/runtime/ruby-resident.d.ts.map +1 -1
  116. package/dist/runtime/ruby-resident.js +2 -0
  117. package/dist/session/nimbus-session.d.ts +13 -8
  118. package/dist/session/nimbus-session.d.ts.map +1 -1
  119. package/dist/session/nimbus-session.js +11 -5
  120. package/dist/session/programmatic.d.ts +25 -30
  121. package/dist/session/programmatic.d.ts.map +1 -1
  122. package/dist/session/programmatic.js +65 -93
  123. package/dist/session/routes.d.ts.map +1 -1
  124. package/dist/session/routes.js +23 -7
  125. package/dist/session/rpc.d.ts +16 -6
  126. package/dist/session/rpc.d.ts.map +1 -1
  127. package/dist/session/rpc.js +53 -13
  128. package/dist/session/start-real-vite.d.ts +24 -9
  129. package/dist/session/start-real-vite.d.ts.map +1 -1
  130. package/dist/session/start-real-vite.js +40 -4
  131. package/dist/session/supervisor-op.d.ts +14 -0
  132. package/dist/session/supervisor-op.d.ts.map +1 -1
  133. package/dist/session/supervisor-op.js +49 -0
  134. package/dist/session/supervisor-rpc.d.ts +11 -0
  135. package/dist/session/supervisor-rpc.d.ts.map +1 -1
  136. package/dist/session/supervisor-rpc.js +18 -0
  137. package/dist/session/vite-command.d.ts.map +1 -1
  138. package/dist/session/vite-command.js +23 -19
  139. package/dist/session/ws-relay.d.ts.map +1 -1
  140. package/dist/session/ws-relay.js +13 -1
  141. package/dist/shell/npm-bin-entrypoints.d.ts +3 -1
  142. package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
  143. package/dist/shell/npm-bin-entrypoints.js +53 -25
  144. package/dist/vfs/facet-resident-store.d.ts.map +1 -1
  145. package/dist/vfs/facet-resident-store.js +11 -5
  146. package/dist/wrangler/nimbus-wrangler.d.ts +8 -0
  147. package/dist/wrangler/nimbus-wrangler.d.ts.map +1 -1
  148. package/dist/wrangler/nimbus-wrangler.js +54 -57
  149. package/package.json +6 -4
  150. package/public/_assets/esbuild-0.24.2.wasm +0 -0
  151. package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/napi-wasm-loader.mjs +4 -4
  152. package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/provenance.json +2 -2
  153. package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.provenance.json +72 -0
  154. package/public/_assets/oxc/nimbus-oxc-1aa8c4c7057c6c28.wasm +0 -0
  155. package/public/_assets/runtime/esbuild-cli-f13e6a112b03ac07.js +1143 -0
  156. package/public/_assets/runtime/js-interpreter-3c37c48024f0f651.js +23 -0
  157. package/public/_assets/runtime/js-interpreter-ops-2f4dfec6798e70fc.js +290 -0
  158. package/public/_assets/runtime/js-interpreter-primordials-e620a61b7e9800fa.js +1 -0
  159. package/public/_assets/runtime/{node-shims-f92f16c7d7c532e9.js → node-shims-e3adc4576da31e6e.js} +651 -103
  160. package/public/_assets/runtime/{esbuild-cli-b7aecffd4c6a36fe.js → oxc-facet-fd86fefcdf16bf9c.js} +217 -1047
  161. package/public/_assets/runtime/{resident-store-92386265e8b306dc.js → resident-store-009f39539ac989fc.js} +11 -5
  162. package/public/_assets/runtime/rolldown-facet-09983434e2dbe93e.js +15165 -0
  163. package/public/_assets/runtime/{vfs-write-ledger-d33ee0034446c3e0.js → vfs-write-ledger-9e9594591425a9b8.js} +4 -1
  164. package/scripts/acorn-primordials.mjs +637 -0
  165. package/scripts/bundle-esbuild-wasm.mjs +42 -50
  166. package/scripts/bundle-facet-workers.mjs +247 -18
  167. package/scripts/bundle-node-shims.mjs +29 -2
  168. package/scripts/bundle-oxc-wasm.mjs +105 -0
  169. package/scripts/interpreter-bundle.mjs +105 -0
  170. package/scripts/napi-wasm/build.mjs +4 -1
  171. package/scripts/napi-wasm/loader/napi-wasm-loader.ts +38 -3
  172. package/scripts/oxc-wasm/Cargo.lock +1246 -0
  173. package/scripts/oxc-wasm/Cargo.toml +30 -0
  174. package/scripts/oxc-wasm/build.mjs +249 -0
  175. package/scripts/oxc-wasm/examples/memory.rs +51 -0
  176. package/scripts/oxc-wasm/examples/transform.rs +20 -0
  177. package/scripts/oxc-wasm/inputs.mjs +32 -0
  178. package/scripts/oxc-wasm/rust-toolchain.toml +5 -0
  179. package/scripts/oxc-wasm/src/abi.rs +136 -0
  180. package/scripts/oxc-wasm/src/diagnostics.rs +49 -0
  181. package/scripts/oxc-wasm/src/helpers.rs +139 -0
  182. package/scripts/oxc-wasm/src/lib.rs +247 -0
  183. package/scripts/oxc-wasm/src/module.rs +978 -0
  184. package/scripts/oxc-wasm/src/names.rs +175 -0
  185. package/scripts/oxc-wasm/src/options.rs +154 -0
  186. package/scripts/parser-audit.mjs +492 -0
  187. package/scripts/rolldown-facet/entry.mjs +21 -0
  188. package/scripts/rolldown-facet/shims.mjs +64 -0
  189. package/dist/facets/esbuild-bundle-pool.d.ts +0 -43
  190. package/dist/facets/esbuild-bundle-pool.d.ts.map +0 -1
  191. package/dist/facets/esbuild-bundle-pool.js +0 -80
  192. package/dist/loaders/pre-bundle-preamble.d.ts +0 -44
  193. package/dist/loaders/pre-bundle-preamble.d.ts.map +0 -1
  194. package/dist/loaders/pre-bundle-preamble.js +0 -116
  195. package/dist/runtime/host-wasm.d.ts +0 -8
  196. package/dist/runtime/host-wasm.d.ts.map +0 -1
  197. package/dist/runtime/host-wasm.js +0 -33
  198. /package/public/_assets/napi-wasm/loader/{99efae93dd1dcc2b → ab7e57fe9e8750c2}/wasi-trampoline.wasm +0 -0
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bundle-oxc-wasm.mjs — stage Nimbus's Oxc transform for the transform facet.
4
+ *
5
+ * scripts/oxc-wasm/build.mjs builds the crate in scripts/oxc-wasm into
6
+ *
7
+ * nimbus-oxc.wasm, provenance.json
8
+ *
9
+ * This script does NOT rebuild (that needs the pinned Rust toolchain), and
10
+ * refuses a wasm whose provenance records other inputs than the tree's
11
+ * (scripts/oxc-wasm/inputs.mjs: the crate, its lockfile and toolchain pin,
12
+ * the recipe and its compiler wrapper), so a Rust change cannot ship the
13
+ * previous build. It stages that pair into the static-assets layer under a name carrying a
14
+ * prefix of the wasm's digest, so no cache layer can serve another build's
15
+ * bytes under it, and pins the digest and size in
16
+ * src/oxc-wasm-artifact.generated.ts, which runtime/oxc-wasm-bytes.ts verifies
17
+ * on every fetch:
18
+ *
19
+ * public/_assets/oxc/nimbus-oxc-<build id>.wasm
20
+ * public/_assets/oxc/nimbus-oxc-<build id>.provenance.json
21
+ *
22
+ * Source: NIMBUS_OXC_WASM_ARTIFACTS (a build.mjs --out directory), else the
23
+ * committed public/_assets/oxc/ (the artifact is tracked, so a checkout
24
+ * without the Rust build re-derives the same generated module).
25
+ *
26
+ * Run via: node scripts/bundle-oxc-wasm.mjs (wired into `bundle`).
27
+ */
28
+
29
+ import { promises as fs } from 'node:fs';
30
+ import path from 'node:path';
31
+ import { createHash } from 'node:crypto';
32
+ import { fileURLToPath } from 'node:url';
33
+ import { changedInputs, oxcWasmInputs } from './oxc-wasm/inputs.mjs';
34
+
35
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
36
+ const ROOT = path.resolve(__dirname, '..');
37
+ const ASSETS = path.join(ROOT, 'public', '_assets', 'oxc');
38
+ const OUT_TS = path.join(ROOT, 'src', 'oxc-wasm-artifact.generated.ts');
39
+
40
+ const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
41
+ const fail = (message) => { throw new Error(`[bundle-oxc-wasm] ${message}`); };
42
+
43
+ /** The wasm and its provenance: a fresh build's, or the committed stage's. */
44
+ async function source() {
45
+ const built = process.env.NIMBUS_OXC_WASM_ARTIFACTS;
46
+ if (built) {
47
+ const dir = path.resolve(built);
48
+ return { wasm: path.join(dir, 'nimbus-oxc.wasm'), provenance: path.join(dir, 'provenance.json') };
49
+ }
50
+ const staged = (await fs.readdir(ASSETS).catch(() => [])).filter((e) => e.endsWith('.wasm'));
51
+ if (staged.length !== 1) fail(`expected one staged wasm under ${ASSETS}, found ${JSON.stringify(staged)}; set NIMBUS_OXC_WASM_ARTIFACTS`);
52
+ const wasm = path.join(ASSETS, staged[0]);
53
+ return { wasm, provenance: wasm.replace(/\.wasm$/, '.provenance.json') };
54
+ }
55
+
56
+ const src = await source();
57
+ const wasm = await fs.readFile(src.wasm);
58
+ const provenanceText = await fs.readFile(src.provenance, 'utf8');
59
+ const provenance = JSON.parse(provenanceText);
60
+ const digest = sha256(wasm);
61
+ if (provenance.artifact !== 'nimbus-oxc') fail(`${src.provenance} describes ${provenance.artifact}, not nimbus-oxc`);
62
+ if (provenance.outputs?.['nimbus-oxc.wasm']?.sha256 !== digest) {
63
+ fail(`${src.wasm} sha256 ${digest} does not match its provenance.json (${provenance.outputs?.['nimbus-oxc.wasm']?.sha256})`);
64
+ }
65
+ const stale = changedInputs(provenance.source?.inputs, await oxcWasmInputs());
66
+ if (stale.length) {
67
+ fail(`${src.wasm} was built from other inputs than this tree's (${stale.join(', ')}); rebuild it with scripts/oxc-wasm/build.mjs and stage that build`);
68
+ }
69
+ const memoryPages = provenance.wasm?.memoryInitialPages;
70
+ if (!Number.isInteger(memoryPages) || memoryPages <= 0) fail(`${src.provenance} records no memoryInitialPages`);
71
+
72
+ const buildId = digest.slice(0, 16);
73
+ const wasmName = `nimbus-oxc-${buildId}.wasm`;
74
+ const provenanceName = `nimbus-oxc-${buildId}.provenance.json`;
75
+ await fs.mkdir(ASSETS, { recursive: true });
76
+ for (const entry of await fs.readdir(ASSETS)) {
77
+ if (entry !== wasmName && entry !== provenanceName) await fs.rm(path.join(ASSETS, entry), { recursive: true, force: true });
78
+ }
79
+ if (path.resolve(src.wasm) !== path.join(ASSETS, wasmName)) {
80
+ await fs.writeFile(path.join(ASSETS, wasmName), wasm);
81
+ await fs.writeFile(path.join(ASSETS, provenanceName), provenanceText);
82
+ }
83
+
84
+ await fs.writeFile(OUT_TS, `/**
85
+ * oxc-wasm-artifact.generated.ts — AUTO-GENERATED by scripts/bundle-oxc-wasm.mjs
86
+ * DO NOT EDIT.
87
+ *
88
+ * Nimbus's Oxc transform (scripts/oxc-wasm, built by its build.mjs; inputs in
89
+ * the staged provenance.json), staged under public/_assets/oxc/. The transform
90
+ * facet's builder (runtime/oxc-wasm-bytes.ts) fetches it and verifies it
91
+ * against OXC_WASM_SHA256 before the loader compiles it. The module imports
92
+ * nothing; its memory starts at OXC_WASM_MEMORY_PAGES pages (stack and static
93
+ * data) and grows only while a transform runs.
94
+ *
95
+ * oxc ${provenance.oxc}, ${provenance.toolchain.split('\n')[0]}, ${(wasm.length / 1048576).toFixed(2)} MiB
96
+ */
97
+
98
+ export const OXC_VERSION: string = ${JSON.stringify(provenance.oxc)};
99
+ export const OXC_WASM_ASSET_PATH: string = ${JSON.stringify(`/_assets/oxc/${wasmName}`)};
100
+ export const OXC_WASM_BUILD_ID: string = ${JSON.stringify(buildId)};
101
+ export const OXC_WASM_SHA256: string = ${JSON.stringify(digest)};
102
+ export const OXC_WASM_BYTES: number = ${wasm.length};
103
+ export const OXC_WASM_MEMORY_PAGES: number = ${memoryPages};
104
+ `);
105
+ console.log(`[bundle-oxc-wasm] staged /_assets/oxc/${wasmName} (${wasm.length} bytes, sha256 ${digest})`);
@@ -0,0 +1,105 @@
1
+ /**
2
+ * interpreter-bundle.mjs — the runtime-code interpreter (@nimbus-sh/core
3
+ * src/interpreter) as a node launch's map carries it: three CommonJS modules.
4
+ *
5
+ * - The primordials (primordials.ts): the built-ins the interpreter calls,
6
+ * captured when the launch starts, before any program code runs. The
7
+ * launch's runtime loads it at its start; the interpreter requires it.
8
+ * - The interpreter, bundled with acorn into one module. It loads late,
9
+ * after a program may have replaced built-ins, so its code must call
10
+ * none while it loads or parses: acorn is bundled rewritten to reach the
11
+ * realm only through captures (acorn-primordials.mjs, parser-realm.ts),
12
+ * and esbuild's CommonJS output would reach it too (its interop
13
+ * helpers capture Object.defineProperty and walk export lists with
14
+ * for-of), so it is bundled as an ES module, which needs no helpers, and
15
+ * its one import and its export statement are then rewritten, by their
16
+ * syntax tree, to a require and a `module.exports` assignment. The
17
+ * bundle states strict mode, which ES modules had implicitly; the
18
+ * interpreter's closures depend on it (an assignment to a read-only
19
+ * property throws, `this` is not coerced).
20
+ * - The host module it runs on (HOST_OPS_SOURCE), which is sloppy at its
21
+ * top level so that interpreted sloppy functions are native sloppy
22
+ * functions; it cannot be part of a strict bundle.
23
+ *
24
+ * esbuild compiles core's TypeScript itself, so all three are built from core
25
+ * src: bundle-node-shims.mjs stages them, and the tests build them the same
26
+ * way to test the source they sit beside.
27
+ */
28
+
29
+ import { parse } from 'acorn';
30
+ import { build } from 'esbuild';
31
+ import { readFile } from 'node:fs/promises';
32
+ import { join } from 'node:path';
33
+
34
+ import { primordialAcorn } from './acorn-primordials.mjs';
35
+ import { resolvePackageDir } from './resolve-package-dir.mjs';
36
+
37
+ /** How the interpreter requires its primordials: the module beside it. */
38
+ export const PRIMORDIALS_FILE = 'interpreter-primordials.js';
39
+
40
+ /**
41
+ * The interpreter's ES module bundle as a CommonJS module: its import of the
42
+ * primordials becomes a require, its export statement `module.exports`.
43
+ */
44
+ function asCommonJs(esm) {
45
+ const program = parse(esm, { ecmaVersion: 'latest', sourceType: 'module' });
46
+ const edits = [];
47
+ for (const node of program.body) {
48
+ if (node.type === 'ImportDeclaration') {
49
+ if (node.source.value !== `./${PRIMORDIALS_FILE}` || node.specifiers.length !== 1 || node.specifiers[0].type !== 'ImportNamespaceSpecifier') {
50
+ throw new Error(`[interpreter-bundle] the interpreter imports ${node.source.value} unexpectedly`);
51
+ }
52
+ edits.push({ start: node.start, end: node.end, text: `const ${node.specifiers[0].local.name} = require(${JSON.stringify(`./${PRIMORDIALS_FILE}`)});` });
53
+ } else if (node.type === 'ExportNamedDeclaration' && !node.declaration && !node.source) {
54
+ const fields = node.specifiers.map((s) => `${JSON.stringify(s.exported.name)}: ${s.local.name}`);
55
+ edits.push({ start: node.start, end: node.end, text: `module.exports = { ${fields.join(', ')} };` });
56
+ } else if (node.type.startsWith('Export')) {
57
+ throw new Error(`[interpreter-bundle] the interpreter bundle has an unexpected ${node.type}`);
58
+ }
59
+ }
60
+ let text = esm;
61
+ for (const edit of edits.sort((a, b) => b.start - a.start)) text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
62
+ return `"use strict";\n${text}`;
63
+ }
64
+
65
+ /**
66
+ * @param {{ start: string }} options `start`: a directory @nimbus-sh/core resolves from.
67
+ * @returns {Promise<{ primordials: string, interpreter: string, ops: string, parser: string }>}
68
+ * `parser`: acorn's module as the rewrite made it, before bundling.
69
+ */
70
+ export async function bundleInterpreter({ start }) {
71
+ const src = join(resolvePackageDir('@nimbus-sh/core', { start }), 'src/interpreter');
72
+ const common = { bundle: true, platform: 'neutral', target: 'esnext', mainFields: ['module', 'main'], minify: true, legalComments: 'none', write: false, metafile: true };
73
+ // esbuild states strict mode in the CommonJS it makes of an ES module.
74
+ const primordials = await build({ ...common, entryPoints: [join(src, 'primordials.ts')], format: 'cjs' });
75
+ let parser = null;
76
+ const interpreter = await build({
77
+ ...common,
78
+ entryPoints: [join(src, 'index.ts')],
79
+ format: 'esm',
80
+ plugins: [{
81
+ name: 'primordials',
82
+ setup(b) {
83
+ b.onResolve({ filter: /^\.\/primordials\.js$/ }, () => ({ path: `./${PRIMORDIALS_FILE}`, external: true }));
84
+ // acorn, rewritten to reach the realm only through parser-realm.ts.
85
+ b.onLoad({ filter: /[\\/]acorn[\\/]dist[\\/]acorn\.mjs$/ }, async (args) => {
86
+ if (parser !== null) throw new Error(`[interpreter-bundle] acorn loaded twice (${args.path})`);
87
+ parser = primordialAcorn(await readFile(args.path, 'utf8'), join(src, 'parser-realm.ts')).code;
88
+ return { contents: parser, loader: 'js' };
89
+ });
90
+ },
91
+ }],
92
+ });
93
+ const inputs = Object.keys(interpreter.metafile.inputs);
94
+ const acornInputs = inputs.filter((path) => /(^|[\\/])acorn[\\/]/.test(path));
95
+ if (parser === null || acornInputs.length !== 1) {
96
+ throw new Error(`[interpreter-bundle] acorn must be bundled once, from dist/acorn.mjs, rewritten; bundled ${acornInputs.join(', ') || 'none'}`);
97
+ }
98
+ // HOST_OPS_SOURCE is built when its module loads: compile and load it to read it.
99
+ const ops = await build({ entryPoints: [join(src, 'host-ops.ts')], bundle: true, format: 'esm', platform: 'neutral', write: false });
100
+ const { HOST_OPS_SOURCE } = await import(`data:text/javascript;base64,${Buffer.from(ops.outputFiles[0].text).toString('base64')}`);
101
+ if (typeof HOST_OPS_SOURCE !== 'string' || HOST_OPS_SOURCE.length === 0) {
102
+ throw new Error('[interpreter-bundle] core src/interpreter/host-ops.ts has no HOST_OPS_SOURCE');
103
+ }
104
+ return { primordials: primordials.outputFiles[0].text, interpreter: asCommonJs(interpreter.outputFiles[0].text), ops: HOST_OPS_SOURCE, parser };
105
+ }
@@ -5,6 +5,8 @@
5
5
  *
6
6
  * bun packages/worker/scripts/napi-wasm/build.mjs --work <dir> --out <dir> [--spec rolldown,satteri]
7
7
  *
8
+ * `--spec none` builds the loader alone (no Rust toolchain needed).
9
+ *
8
10
  * Keep this Bun orchestrator at <=4 GiB. If Cargo needs a separate 16 GiB
9
11
  * cgroup, name the bounded runner explicitly; builds remain serial:
10
12
  * NIMBUS_CARGO_RUNNER=/mnt/scratch/nimbus/run-bounded \
@@ -60,7 +62,8 @@ function arg(name, required = true) {
60
62
  }
61
63
  const WORK = path.resolve(arg('--work'));
62
64
  const OUT = path.resolve(arg('--out'));
63
- const SELECTED = (arg('--spec', false) ?? Object.keys(SPECS).join(',')).split(',');
65
+ const specArg = arg('--spec', false) ?? Object.keys(SPECS).join(',');
66
+ const SELECTED = specArg === 'none' ? [] : specArg.split(',');
64
67
  for (const name of SELECTED) if (!SPECS[name]) throw new Error(`napi-wasm: no spec named ${name}`);
65
68
 
66
69
  const sha256 = (bytes) => createHash('sha256').update(bytes).digest('hex');
@@ -63,8 +63,22 @@ export interface NapiWasmBindingHost {
63
63
  trampoline: WebAssembly.Module;
64
64
  /** Wasm pages the binding's imported memory starts at (its declared minimum). */
65
65
  memoryPages: number;
66
+ /**
67
+ * The binding's linear memory, when the host makes it (to watch what the
68
+ * binding grows to): `memoryPages` initial pages, maximum 65536, unshared.
69
+ * Absent: the loader makes it.
70
+ */
71
+ memory?: WebAssembly.Memory;
66
72
  /** The binding's name, for diagnostics. */
67
73
  name: string;
74
+ /**
75
+ * Called once if the binding dies: a trap (a Rust panic aborts), or the
76
+ * host's stack overflowing inside it (a RangeError). The instance is
77
+ * unusable from then on and no promise it holds will settle, so the host
78
+ * settles its own callers and drops the binding. Absent: the error is
79
+ * thrown as an uncaught one.
80
+ */
81
+ onFatal?(error: unknown): void;
68
82
  }
69
83
 
70
84
  /** What the loader calls on the binding instance besides napi. */
@@ -500,7 +514,7 @@ export function createNapiWasmBinding(host: NapiWasmBindingHost): Record<string,
500
514
  // stack may suspend, so only such a binding takes the JSPI trampoline.
501
515
  const pumped = WebAssembly.Module.exports(host.binding).some((e) => e.name === 'nimbus_napi_pump');
502
516
  const jspi = pumped && typeof WebAssembly.Suspending === 'function' && typeof WebAssembly.promising === 'function';
503
- const memory = new WebAssembly.Memory({ initial: host.memoryPages, maximum: 65536 });
517
+ const memory = host.memory ?? new WebAssembly.Memory({ initial: host.memoryPages, maximum: 65536 });
504
518
  const getMemory = () => memory;
505
519
  const fds = new Map<number, FilesystemFd>([
506
520
  [0, { kind: 'stdin' }],
@@ -562,9 +576,15 @@ export function createNapiWasmBinding(host: NapiWasmBindingHost): Record<string,
562
576
  let fatal: unknown = null;
563
577
 
564
578
  const die = (error: unknown) => {
579
+ if (fatal !== null) return;
565
580
  fatal = error;
566
- // A trap (a Rust panic aborts) leaves the instance unusable. Surface it
567
- // as an uncaught error rather than leave every pending promise hanging.
581
+ while (waiting > 0) context.decreaseWaitingRequestCounter();
582
+ // A trap or a stack overflow leaves the instance unusable: tell the host,
583
+ // or surface it as an uncaught error.
584
+ if (host.onFatal) {
585
+ host.onFatal(error);
586
+ return;
587
+ }
568
588
  queueMicrotask(() => {
569
589
  throw error;
570
590
  });
@@ -636,6 +656,21 @@ export function createNapiWasmBinding(host: NapiWasmBindingHost): Record<string,
636
656
  };
637
657
 
638
658
  const context = createContext();
659
+ // What the binding holds open (threadsafe functions, async work) holds a
660
+ // Node host's event loop open until released; a dead binding releases
661
+ // nothing, so a fatal error lets go of it all.
662
+ let waiting = 0;
663
+ const increaseWaiting = context.increaseWaitingRequestCounter.bind(context);
664
+ const decreaseWaiting = context.decreaseWaitingRequestCounter.bind(context);
665
+ context.increaseWaitingRequestCounter = () => {
666
+ waiting++;
667
+ increaseWaiting();
668
+ };
669
+ context.decreaseWaitingRequestCounter = () => {
670
+ if (waiting === 0) return;
671
+ waiting--;
672
+ decreaseWaiting();
673
+ };
639
674
  const { napiModule } = instantiateNapiModuleSync(host.binding, {
640
675
  context,
641
676
  asyncWorkPoolSize: 0,