sandboxedjs 0.1.73 → 0.1.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -3,14 +3,14 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var pako = require('pako');
6
+ var acorn = require('acorn');
7
+ var jsx = require('acorn-jsx');
6
8
  var sha256 = require('@noble/hashes/sha256');
7
9
  var index_js = require('buffer/index.js');
8
10
  var sha512 = require('@noble/hashes/sha512');
9
11
  var sha1 = require('@noble/hashes/sha1');
10
12
  var semver = require('semver');
11
- var acorn = require('acorn');
12
13
  var EventEmitter4 = require('events/events.js');
13
- var jsx = require('acorn-jsx');
14
14
  var resolve_exports = require('resolve.exports');
15
15
  var pathModule = require('path-browserify');
16
16
  var streamModule4 = require('stream-browserify');
@@ -25,8 +25,8 @@ var hmac = require('@noble/hashes/hmac');
25
25
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
26
26
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
27
27
 
28
- var EventEmitter4__default = /*#__PURE__*/_interopDefault(EventEmitter4);
29
28
  var jsx__default = /*#__PURE__*/_interopDefault(jsx);
29
+ var EventEmitter4__default = /*#__PURE__*/_interopDefault(EventEmitter4);
30
30
  var pathModule__default = /*#__PURE__*/_interopDefault(pathModule);
31
31
  var streamModule4__default = /*#__PURE__*/_interopDefault(streamModule4);
32
32
  var processShim__default = /*#__PURE__*/_interopDefault(processShim);
@@ -3824,7 +3824,10 @@ function builtinKill({ shell, argv, io }) {
3824
3824
  for (const target of args.slice(i)) {
3825
3825
  if (target.startsWith("%")) {
3826
3826
  const job = shell.jobs.find((j) => j.id === Number(target.slice(1)));
3827
- if (job) continue;
3827
+ if (job) {
3828
+ if (job.state !== "done") shell.kernel.procs.signal(job.pgid, signal);
3829
+ continue;
3830
+ }
3828
3831
  io.stderr.write(`kill: ${target}: no such job
3829
3832
  `);
3830
3833
  status = 1;
@@ -5420,6 +5423,51 @@ var BufferSink = class {
5420
5423
  this.total = 0;
5421
5424
  }
5422
5425
  };
5426
+ var DetachableOutput = class {
5427
+ constructor(inner, onDetached) {
5428
+ this.inner = inner;
5429
+ this.onDetached = onDetached;
5430
+ }
5431
+ inner;
5432
+ onDetached;
5433
+ attached = true;
5434
+ decoder = new TextDecoder();
5435
+ get closed() {
5436
+ return this.attached ? this.inner.closed : false;
5437
+ }
5438
+ get isTTY() {
5439
+ return this.inner.isTTY;
5440
+ }
5441
+ set isTTY(value) {
5442
+ this.inner.isTTY = value;
5443
+ }
5444
+ get columns() {
5445
+ return this.inner.columns;
5446
+ }
5447
+ set columns(value) {
5448
+ this.inner.columns = value;
5449
+ }
5450
+ get rows() {
5451
+ return this.inner.rows;
5452
+ }
5453
+ set rows(value) {
5454
+ this.inner.rows = value;
5455
+ }
5456
+ write(data) {
5457
+ if (this.attached) {
5458
+ this.inner.write(data);
5459
+ return;
5460
+ }
5461
+ if (!this.onDetached) return;
5462
+ this.onDetached(typeof data === "string" ? data : this.decoder.decode(data, { stream: true }));
5463
+ }
5464
+ end() {
5465
+ if (this.attached) this.inner.end();
5466
+ }
5467
+ detach() {
5468
+ this.attached = false;
5469
+ }
5470
+ };
5423
5471
  var CallbackSink = class {
5424
5472
  constructor(sink, opts = {}) {
5425
5473
  this.sink = sink;
@@ -7282,6 +7330,77 @@ function createSysProvider(kernel) {
7282
7330
  };
7283
7331
  }
7284
7332
 
7333
+ // src/net/policy.ts
7334
+ function isLoopbackHostname(hostname) {
7335
+ const host2 = hostname.replace(/^\[|\]$/g, "").toLowerCase();
7336
+ return host2 === "localhost" || host2 === "0.0.0.0" || host2 === "::1" || host2 === "::" || host2.startsWith("127.") || host2.endsWith(".localhost");
7337
+ }
7338
+ function outboundAllowed(policy, url) {
7339
+ if (!policy.allowOutbound) return false;
7340
+ let hostname;
7341
+ try {
7342
+ hostname = new URL(url).hostname;
7343
+ } catch {
7344
+ return false;
7345
+ }
7346
+ if (isLoopbackHostname(hostname)) return false;
7347
+ const hosts = policy.allowedHosts;
7348
+ if (!hosts) return true;
7349
+ return hosts.some((allowed) => hostname === allowed || hostname.endsWith(`.${allowed}`));
7350
+ }
7351
+ function outboundBlockedError(url) {
7352
+ let host2 = url;
7353
+ try {
7354
+ host2 = new URL(url).host;
7355
+ } catch {
7356
+ }
7357
+ return Object.assign(
7358
+ new Error(
7359
+ `connect ENETUNREACH ${host2}: outbound network access is disabled for this container (allowed by the host with network.allowOutbound and network.allowedHosts)`
7360
+ ),
7361
+ { code: "ENETUNREACH", errno: -101, syscall: "connect", hostname: host2 }
7362
+ );
7363
+ }
7364
+ function requestUrl(input) {
7365
+ if (typeof input === "string") return input;
7366
+ if (input instanceof URL) return input.href;
7367
+ return input.url;
7368
+ }
7369
+ function policedFetch(policy, fetchImpl) {
7370
+ return (async (input, init) => {
7371
+ const url = requestUrl(input);
7372
+ if (!outboundAllowed(policy, url)) {
7373
+ throw Object.assign(new TypeError("fetch failed"), { cause: outboundBlockedError(url) });
7374
+ }
7375
+ return fetchImpl(input, init);
7376
+ });
7377
+ }
7378
+ function policedWebSocket(Native, policy) {
7379
+ const Base = Native;
7380
+ return class PolicedWebSocket extends Base {
7381
+ constructor(url, protocols) {
7382
+ const href = url instanceof URL ? url.href : String(url);
7383
+ let parsed = null;
7384
+ try {
7385
+ parsed = new URL(href);
7386
+ } catch {
7387
+ }
7388
+ if (parsed && isLoopbackHostname(parsed.hostname)) {
7389
+ throw Object.assign(
7390
+ new Error(
7391
+ `WebSocket to ${parsed.host} is not available from a program inside the container; reach servers in the container over HTTP`
7392
+ ),
7393
+ { code: "ERR_UNSUPPORTED" }
7394
+ );
7395
+ }
7396
+ if (parsed && !outboundAllowed(policy, href.replace(/^ws(s?):/i, "http$1:"))) {
7397
+ throw outboundBlockedError(href);
7398
+ }
7399
+ super(url, protocols);
7400
+ }
7401
+ };
7402
+ }
7403
+
7285
7404
  // src/net/stack.ts
7286
7405
  var NetworkStack = class {
7287
7406
  constructor(pod, vfs, options = {}) {
@@ -7420,16 +7539,12 @@ var NetworkStack = class {
7420
7539
  return false;
7421
7540
  }
7422
7541
  // ── outbound policy ──────────────────────────────────────────────────────
7542
+ /** The policy every way out of the container applies, not only the shell's. */
7543
+ get policy() {
7544
+ return { allowOutbound: this.options.allowOutbound, allowedHosts: this.options.allowedHosts ?? null };
7545
+ }
7423
7546
  outboundAllowed(url) {
7424
- if (!this.options.allowOutbound) return false;
7425
- const hosts = this.options.allowedHosts;
7426
- if (hosts === null || hosts === void 0) return true;
7427
- try {
7428
- const { hostname } = new URL(url);
7429
- return hosts.some((h) => hostname === h || hostname.endsWith("." + h));
7430
- } catch {
7431
- return false;
7432
- }
7547
+ return outboundAllowed(this.policy, url);
7433
7548
  }
7434
7549
  // ── /proc plumbing ───────────────────────────────────────────────────────
7435
7550
  procNetDev() {
@@ -8577,11 +8692,16 @@ var Shell = class _Shell {
8577
8692
  }
8578
8693
  return path;
8579
8694
  }
8580
- /** A copy that shares nothing mutable with this shell. */
8581
- fork() {
8695
+ /**
8696
+ * A copy that shares nothing mutable with this shell.
8697
+ *
8698
+ * Subshells and pipeline stages run as part of this shell's process. A
8699
+ * background job passes a process of its own, the way fork(2) gives one.
8700
+ */
8701
+ fork(proc = this.proc) {
8582
8702
  const sub = new _Shell({
8583
8703
  kernel: this.kernel,
8584
- proc: this.proc,
8704
+ proc,
8585
8705
  cwd: this.cwd,
8586
8706
  cred: this.cred,
8587
8707
  env: {},
@@ -8736,29 +8856,42 @@ sys ${fmt2(Math.floor(ms * 0.2))}
8736
8856
  `);
8737
8857
  }
8738
8858
  startBackgroundJob(node2, io) {
8739
- const sub = this.fork();
8859
+ const jobProc = this.kernel.procs.create({
8860
+ argv: [this.scriptName],
8861
+ cwd: this.cwd,
8862
+ env: this.vars.environment(),
8863
+ cred: this.cred,
8864
+ ppid: this.proc.pid,
8865
+ kind: "shell",
8866
+ tty: this.proc.tty,
8867
+ stdio: { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr }
8868
+ });
8869
+ const sub = this.fork(jobProc);
8740
8870
  const id = this.nextJobId++;
8741
8871
  const command = describeNode(node2);
8742
8872
  const job = {
8743
8873
  id,
8744
- pgid: this.proc.pid + id,
8874
+ pgid: jobProc.pid,
8745
8875
  command,
8746
8876
  state: "running",
8747
8877
  exitCode: null,
8748
- pids: [],
8878
+ pids: [jobProc.pid],
8749
8879
  promise: Promise.resolve(0)
8750
8880
  };
8751
8881
  job.promise = (async () => {
8882
+ let code;
8752
8883
  try {
8753
- const code = await sub.run(node2, { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr });
8754
- job.exitCode = code;
8755
- job.state = "done";
8756
- return code;
8884
+ code = await sub.run(node2, { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr });
8757
8885
  } catch (e) {
8758
- job.state = "done";
8759
- job.exitCode = e instanceof ShellExit ? e.code : 1;
8760
- return job.exitCode;
8886
+ code = e instanceof ShellExit ? e.code : 1;
8761
8887
  }
8888
+ jobProc.exit(code);
8889
+ job.exitCode = jobProc.exitCode ?? code;
8890
+ job.state = "done";
8891
+ queueMicrotask(() => {
8892
+ if (jobProc.children.size === 0) this.kernel.procs.remove(jobProc.pid);
8893
+ });
8894
+ return job.exitCode;
8762
8895
  })();
8763
8896
  this.jobs.push(job);
8764
8897
  this.lastBackgroundPid = job.pgid;
@@ -17495,437 +17628,1110 @@ var commands11 = [xsel, xclip, pbcopy, pbpaste, wlCopy, wlPaste];
17495
17628
 
17496
17629
  // src/node/node.ts
17497
17630
  init_path();
17498
- var NODE_VERSION = "v22.12.0";
17499
- new TextEncoder();
17500
- var tempCounter = 0;
17501
- function tempPath(kind, pid, ext = ".js") {
17502
- return `/tmp/.sbx-${kind}-${pid}-${tempCounter++}${ext}`;
17503
- }
17504
- function withPrelude(source, prelude) {
17505
- if (!source.startsWith("#!")) return prelude + source;
17506
- const eol = source.indexOf("\n");
17507
- if (eol < 0) return source + "\n" + prelude;
17508
- return source.slice(0, eol + 1) + prelude + source.slice(eol + 1);
17509
- }
17510
- function siblingPath(script, pid) {
17511
- const ext = extname(script) || ".js";
17512
- return join(dirname(script), `.sbx-${pid}-${tempCounter++}-${basename(script, ext)}${ext}`);
17513
- }
17514
- var BUFFER_COMPAT_SHIM = `
17515
- (function () {
17516
- const seen = new Set();
17517
- const widen = (B) => {
17518
- if (!B || seen.has(B) || typeof B.isBuffer !== 'function') return;
17519
- seen.add(B);
17520
- const original = B.isBuffer.bind(B);
17521
- B.isBuffer = (value) => original(value) || value instanceof Uint8Array;
17522
- };
17523
- try { widen(globalThis.Buffer); } catch (e) {}
17524
- try { widen(require('buffer').Buffer); } catch (e) {}
17525
- })();
17526
- `;
17527
- function buildPrelude(scriptPath, argv, stdinPath) {
17528
- const argvLiteral = JSON.stringify(["/usr/bin/node", scriptPath, ...argv]);
17529
- const pathLiteral = JSON.stringify(scriptPath);
17530
- const stdinLiteral = JSON.stringify(stdinPath);
17531
- return `${BUFFER_COMPAT_SHIM}
17532
- process.argv = ${argvLiteral};
17533
- try {
17534
- __filename = ${pathLiteral};
17535
- module.filename = ${pathLiteral};
17536
- module.id = ${pathLiteral};
17537
- if (require.main === module) require.main.filename = ${pathLiteral};
17538
- } catch (e) {}
17539
- ${stdinPath === null ? "" : `(function () {
17540
- const fs = require('fs');
17541
- const { Readable } = require('stream');
17542
- let data;
17543
- try { data = fs.readFileSync(${stdinLiteral}); } catch (e) { data = Buffer.alloc(0); }
17544
- const stream = Readable.from([data]);
17545
- stream.isTTY = false;
17546
- stream.fd = 0;
17547
- stream.setRawMode = function () { return this; };
17548
- try {
17549
- Object.defineProperty(process, 'stdin', { value: stream, configurable: true, writable: true });
17550
- } catch (e) { process.stdin = stream; }
17551
- })();
17552
- `}`;
17631
+ var HELPERS = {
17632
+ /** Import a specifier and return an ES-module-shaped namespace. */
17633
+ import: "__sbxImport",
17634
+ /** `import(...)`, returning a promise of a namespace. */
17635
+ dynamic: "__sbxDynamicImport",
17636
+ /** `export * from` — copy live bindings onto `exports`. */
17637
+ exportAll: "__sbxExportAll",
17638
+ /** `import.meta`. */
17639
+ meta: "__sbxMeta",
17640
+ /**
17641
+ * The exports object, under a name of the engine's choosing.
17642
+ *
17643
+ * An ES module is free to declare its own top-level `exports`, `require` or
17644
+ * `__dirname` they are ordinary identifiers there, and real packages use
17645
+ * all three (`const require = createRequire(import.meta.url)` is close to
17646
+ * idiomatic). Naming the wrapper's binding something no source would write
17647
+ * removes that entire class of collision instead of patching it up after a
17648
+ * `SyntaxError`.
17649
+ */
17650
+ exports: "__sbxExports"
17651
+ };
17652
+ var DEFAULT_LOCAL = "__sbxDefault";
17653
+ var MAYBE_ESM = /(^|[\s;}(])(?:import|export)(?:[\s({[*"']|$)|\bimport\s*\.\s*meta\b/;
17654
+ function looksLikeEsm(source) {
17655
+ return MAYBE_ESM.test(source);
17553
17656
  }
17554
- async function execute(ctx, invocation) {
17555
- const { pod } = ctx.kernel;
17556
- const live = ctx.stdin.interactive || ctx.stdin.isTTY;
17557
- const tty = ctx.stdin.isTTY;
17558
- let exitCode = 0;
17657
+ function transformEsm(source, filename = "module.js") {
17658
+ if (!looksLikeEsm(source)) return null;
17659
+ let ast;
17559
17660
  try {
17560
- const proc = await pod.spawn("node", [invocation.script], {
17561
- cwd: ctx.cwd,
17562
- env: { ...ctx.env, PWD: ctx.cwd },
17563
- argv: ["/usr/bin/node", invocation.argvPath ?? invocation.script, ...invocation.argv],
17564
- ...invocation.stdinPath ? { stdinPath: invocation.stdinPath } : {},
17565
- /* An interactive caller's input was not captured to a file, so the
17566
- * program's stdin has to stay open and be fed as it arrives — and be
17567
- * reported as a terminal, since that is what decides whether a CLI
17568
- * prompts or takes its defaults. */
17569
- ...live ? { interactiveStdin: true } : {},
17570
- ...tty ? { tty: true } : {}
17571
- });
17572
- proc.on("output", (chunk) => {
17573
- try {
17574
- ctx.write(chunk);
17575
- } catch {
17576
- }
17577
- });
17578
- proc.on("error", (chunk) => {
17579
- try {
17580
- ctx.stderr.write(chunk);
17581
- } catch {
17582
- }
17583
- });
17584
- proc.on("rawmode", (enabled) => {
17585
- ctx.stdin.rawMode = enabled;
17586
- });
17587
- let forwarding = false;
17588
- if (live) {
17589
- forwarding = true;
17590
- void (async () => {
17591
- while (forwarding) {
17592
- const chunk = await ctx.stdin.read();
17593
- if (chunk === null) {
17594
- proc.endInput?.();
17595
- break;
17596
- }
17597
- try {
17598
- proc.write(new TextDecoder().decode(chunk));
17599
- } catch {
17600
- break;
17601
- }
17602
- }
17603
- })();
17604
- }
17605
- let onAbort;
17606
- const aborted = new Promise((resolve3) => {
17607
- onAbort = () => {
17608
- try {
17609
- proc.kill();
17610
- } catch {
17611
- }
17612
- resolve3({ exitCode: 137 });
17613
- };
17614
- if (ctx.signal.aborted) onAbort();
17615
- else ctx.signal.addEventListener("abort", onAbort, { once: true });
17616
- });
17617
- const result = await Promise.race([proc.completion, aborted]);
17618
- forwarding = false;
17619
- if (onAbort) ctx.signal.removeEventListener("abort", onAbort);
17620
- exitCode = result.exitCode;
17621
- } catch (e) {
17622
- ctx.stderr.write(`node: ${e instanceof Error ? e.message : String(e)}
17623
- `);
17624
- exitCode = 1;
17625
- } finally {
17626
- for (const temp of invocation.temps) {
17627
- try {
17628
- ctx.vfs.unlink(temp);
17629
- } catch {
17630
- }
17631
- }
17661
+ ast = parseModule(source, filename);
17662
+ } catch {
17663
+ return null;
17632
17664
  }
17633
- return exitCode;
17634
- }
17635
- async function captureStdin(ctx) {
17636
- if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
17637
- const bytes2 = await ctx.stdin.readAll();
17638
- if (bytes2.length === 0) return null;
17639
- const path = tempPath("stdin", ctx.proc.pid, ".dat");
17640
- ctx.vfs.writeFile(path, bytes2, { privileged: true, mode: 384 });
17641
- return path;
17642
- }
17643
- var node = defineCommand({
17644
- name: "node",
17645
- path: "/usr/bin/node",
17646
- aliases: ["nodejs"],
17647
- summary: "run a JavaScript program with Node.js",
17648
- usage: "node [options] [script.js] [arguments]",
17649
- manual: `Runs JavaScript with a real Node.js API surface \u2014 fs, path, http,
17650
- crypto, streams, worker_threads and npm packages \u2014 inside the container.
17651
- The filesystem a script sees is the container's filesystem.`,
17652
- async run(ctx) {
17653
- const argv = ctx.args;
17654
- const temps = [];
17655
- let i = 0;
17656
- let evalSource;
17657
- let printResult = false;
17658
- let scriptArg;
17659
- for (; i < argv.length; i++) {
17660
- const arg = argv[i];
17661
- if (arg === "-v" || arg === "--version") {
17662
- ctx.line(NODE_VERSION);
17663
- return 0;
17664
- }
17665
- if (arg === "-h" || arg === "--help") {
17666
- printHelp(ctx);
17667
- return 0;
17668
- }
17669
- if (arg === "-e" || arg === "--eval") {
17670
- evalSource = argv[++i] ?? "";
17671
- i++;
17672
- break;
17673
- }
17674
- if (arg === "-p" || arg === "--print") {
17675
- printResult = true;
17676
- evalSource = argv[++i] ?? "";
17677
- i++;
17678
- break;
17679
- }
17680
- if (arg.startsWith("--eval=")) {
17681
- evalSource = arg.slice("--eval=".length);
17682
- i++;
17665
+ const body = ast.body;
17666
+ const hasModuleSyntax = body.some(
17667
+ (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
17668
+ );
17669
+ const usesImportMeta = !hasModuleSyntax && containsImportMeta(ast);
17670
+ const esm = hasModuleSyntax || usesImportMeta;
17671
+ if (!esm && !containsImportExpression(ast)) return null;
17672
+ const edits = [];
17673
+ const prelude = [];
17674
+ const importBindings = /* @__PURE__ */ new Map();
17675
+ const exportGetters = /* @__PURE__ */ new Map();
17676
+ let namespaceCount = 0;
17677
+ const namespaceFor = (specifier) => {
17678
+ const id = `__sbxNs${namespaceCount++}`;
17679
+ prelude.push(`var ${id} = ${HELPERS.import}(${JSON.stringify(specifier)});`);
17680
+ return id;
17681
+ };
17682
+ for (const node2 of body) {
17683
+ if (node2.type === "ImportDeclaration") collectImport(node2);
17684
+ }
17685
+ for (const node2 of body) {
17686
+ switch (node2.type) {
17687
+ case "ExportNamedDeclaration":
17688
+ collectNamedExport(node2);
17683
17689
  break;
17684
- }
17685
- if (arg.startsWith("--print=")) {
17686
- printResult = true;
17687
- evalSource = arg.slice("--print=".length);
17688
- i++;
17690
+ case "ExportDefaultDeclaration":
17691
+ collectDefaultExport(node2);
17689
17692
  break;
17690
- }
17691
- if (arg === "--") {
17692
- i++;
17693
+ case "ExportAllDeclaration":
17694
+ collectExportAll(node2);
17693
17695
  break;
17694
- }
17695
- if (arg.startsWith("-") && arg !== "-") {
17696
- continue;
17697
- }
17698
- scriptArg = arg;
17699
- i++;
17700
- break;
17701
- }
17702
- const scriptArgs = argv.slice(i);
17703
- if (evalSource !== void 0) {
17704
- const body = printResult ? `const __sbxResult = (function(){ return (${evalSource}
17705
- ); })();
17706
- console.log(typeof __sbxResult === 'string' ? __sbxResult : require('util').inspect(__sbxResult));` : evalSource;
17707
- const path = tempPath("eval", ctx.proc.pid);
17708
- const stdinPath2 = await captureStdin(ctx);
17709
- if (stdinPath2) temps.push(stdinPath2);
17710
- ctx.vfs.writeFile(path, buildBootstrapForEval(body, ["/usr/bin/node", ...scriptArgs], stdinPath2), {
17711
- privileged: true,
17712
- mode: 384
17713
- });
17714
- temps.push(path);
17715
- return execute(ctx, { script: path, argv: scriptArgs, stdinPath: stdinPath2, temps });
17716
17696
  }
17717
- if (scriptArg === void 0 || scriptArg === "-") {
17718
- if (ctx.stdin.isTTY) {
17719
- ctx.line(`Welcome to Node.js ${NODE_VERSION}.`);
17720
- ctx.line('Type ".help" for more information.');
17721
- return runRepl(ctx);
17697
+ }
17698
+ rewriteReferences();
17699
+ const header = esm ? [
17700
+ `Object.defineProperty(${HELPERS.exports}, "__esModule", { value: true });`,
17701
+ ...prelude,
17702
+ ...[...exportGetters].map(
17703
+ ([name, expression]) => `Object.defineProperty(${HELPERS.exports}, ${JSON.stringify(name)}, { enumerable: true, configurable: true, get: function () { return ${expression}; } });`
17704
+ )
17705
+ ].join("\n") : "";
17706
+ return {
17707
+ code: header ? `${header}
17708
+ ${applyEdits(source, edits)}` : applyEdits(source, edits),
17709
+ esm,
17710
+ topLevelAwait: hasTopLevelAwait(ast)
17711
+ };
17712
+ function collectImport(node2) {
17713
+ const specifier = node2.source.value;
17714
+ const specifiers = node2.specifiers ?? [];
17715
+ const id = namespaceFor(specifier);
17716
+ for (const entry of specifiers) {
17717
+ const local = entry.local.name ?? "";
17718
+ if (entry.type === "ImportDefaultSpecifier") {
17719
+ importBindings.set(local, `${id}.default`);
17720
+ } else if (entry.type === "ImportNamespaceSpecifier") {
17721
+ importBindings.set(local, id);
17722
+ } else {
17723
+ const imported = entry.imported;
17724
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
17725
+ importBindings.set(local, `${id}[${JSON.stringify(name)}]`);
17722
17726
  }
17723
- const source = new TextDecoder().decode(await ctx.stdin.readAll());
17724
- if (source.trim() === "") return 0;
17725
- const path = tempPath("stdin-script", ctx.proc.pid);
17726
- ctx.vfs.writeFile(path, buildBootstrapForEval(source, ["/usr/bin/node", ...scriptArgs], null), {
17727
- privileged: true,
17728
- mode: 384
17729
- });
17730
- temps.push(path);
17731
- return execute(ctx, { script: path, argv: scriptArgs, temps });
17732
17727
  }
17733
- const script = resolveScript(ctx, scriptArg);
17734
- if (!script) {
17735
- ctx.stderr.write(
17736
- `node:internal/modules/cjs/loader:1215
17737
- throw err;
17738
- ^
17739
-
17740
- Error: Cannot find module '${ctx.path(scriptArg)}'
17741
- `
17742
- );
17743
- return 1;
17728
+ drop(node2);
17729
+ }
17730
+ function collectNamedExport(node2) {
17731
+ const declaration = node2.declaration;
17732
+ if (declaration) {
17733
+ for (const name of declaredNames(declaration)) exportGetters.set(name, name);
17734
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17735
+ return;
17744
17736
  }
17745
- const stdinPath = await captureStdin(ctx);
17746
- if (stdinPath) temps.push(stdinPath);
17747
- const sibling = siblingPath(script, ctx.proc.pid);
17748
- try {
17749
- const source = ctx.vfs.readText(script, ctx.cred);
17750
- ctx.vfs.writeFile(sibling, withPrelude(source, buildPrelude(script, scriptArgs, stdinPath)), {
17751
- privileged: true,
17752
- mode: 384
17753
- });
17754
- temps.push(sibling);
17755
- return await execute(ctx, { script: sibling, argvPath: script, argv: scriptArgs, stdinPath, temps });
17756
- } catch {
17757
- return await execute(ctx, { script, argv: scriptArgs, stdinPath, temps });
17737
+ const source_ = node2.source;
17738
+ const id = source_ ? namespaceFor(source_.value) : null;
17739
+ for (const entry of node2.specifiers ?? []) {
17740
+ const local = nameOf(entry.local);
17741
+ const exported = nameOf(entry.exported);
17742
+ const read = id ? `${id}[${JSON.stringify(local)}]` : importBindings.get(local) ?? local;
17743
+ exportGetters.set(exported, read);
17758
17744
  }
17745
+ drop(node2);
17759
17746
  }
17760
- });
17761
- function buildBootstrapForEval(source, argv, stdinPath) {
17762
- const prelude = stdinPath ? `const fs = require('fs');
17763
- const { Readable } = require('stream');
17764
- let __sbxData; try { __sbxData = fs.readFileSync(${JSON.stringify(stdinPath)}); } catch (e) { __sbxData = Buffer.alloc(0); }
17765
- const __sbxStdin = Readable.from([__sbxData]);
17766
- __sbxStdin.isTTY = false;
17767
- __sbxStdin.setRawMode = function () { return this; };
17768
- try { Object.defineProperty(process, 'stdin', { value: __sbxStdin, configurable: true, writable: true }); } catch (e) { process.stdin = __sbxStdin; }
17769
- ` : "";
17770
- return `${BUFFER_COMPAT_SHIM}process.argv = ${JSON.stringify(argv)};
17771
- ${prelude}${source}
17772
- `;
17773
- }
17774
- function resolveScript(ctx, operand) {
17775
- const base2 = ctx.path(operand);
17776
- const candidates = [base2, `${base2}.js`, `${base2}.mjs`, `${base2}.cjs`, `${base2}.json`];
17777
- for (const candidate of candidates) {
17778
- try {
17779
- const st = ctx.vfs.stat(candidate, { cred: ctx.cred });
17780
- if (st.isFile()) return ctx.vfs.realpath(candidate, ctx.cred);
17781
- if (st.isDirectory()) {
17782
- const manifest = join(candidate, "package.json");
17783
- if (ctx.vfs.lexists(manifest)) {
17784
- try {
17785
- const main = JSON.parse(ctx.vfs.readText(manifest, ctx.cred)).main;
17786
- if (main) {
17787
- const mainPath = resolve(candidate, main);
17788
- if (ctx.vfs.lexists(mainPath)) return ctx.vfs.realpath(mainPath, ctx.cred);
17789
- if (ctx.vfs.lexists(mainPath + ".js")) return ctx.vfs.realpath(mainPath + ".js", ctx.cred);
17790
- }
17791
- } catch {
17792
- }
17793
- }
17794
- for (const index of ["index.js", "index.mjs", "index.cjs"]) {
17795
- const path = join(candidate, index);
17796
- if (ctx.vfs.lexists(path)) return ctx.vfs.realpath(path, ctx.cred);
17797
- }
17747
+ function collectDefaultExport(node2) {
17748
+ const declaration = node2.declaration;
17749
+ const isDeclaration = declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration";
17750
+ if (isDeclaration) {
17751
+ const named = declaration.id;
17752
+ if (named) {
17753
+ exportGetters.set("default", named.name);
17754
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17755
+ return;
17798
17756
  }
17799
- } catch {
17757
+ const keyword = declaration.type === "FunctionDeclaration" ? "function" : "class";
17758
+ const keywordEnd = source.indexOf(keyword, declaration.start) + keyword.length;
17759
+ exportGetters.set("default", DEFAULT_LOCAL);
17760
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17761
+ edits.push({ start: keywordEnd, end: keywordEnd, text: ` ${DEFAULT_LOCAL}` });
17762
+ return;
17800
17763
  }
17764
+ exportGetters.set("default", DEFAULT_LOCAL);
17765
+ edits.push({ start: node2.start, end: declaration.start, text: `var ${DEFAULT_LOCAL} = ` });
17766
+ edits.push({ start: node2.end, end: node2.end, text: ";" });
17801
17767
  }
17802
- return null;
17803
- }
17804
- async function runRepl(ctx) {
17805
- const historyPath = join(ctx.env.HOME ?? "/root", ".node_repl_history");
17806
- const statements = [];
17807
- for (; ; ) {
17808
- ctx.write("> ");
17809
- const line = await ctx.stdin.readLine();
17810
- if (line === null) {
17811
- ctx.line("");
17812
- break;
17813
- }
17814
- const trimmed = line.trim();
17815
- if (trimmed === ".exit") break;
17816
- if (trimmed === ".help") {
17817
- ctx.line(".exit Exit the REPL");
17818
- ctx.line(".help Print this help message");
17819
- ctx.line(".clear Reset the session context");
17820
- continue;
17821
- }
17822
- if (trimmed === ".clear") {
17823
- statements.length = 0;
17824
- continue;
17825
- }
17826
- if (trimmed === "") continue;
17827
- const program = [...statements, `console.log(require('util').inspect((${trimmed})))`].join(";\n");
17828
- const fallback = [...statements, trimmed].join(";\n");
17829
- const path = tempPath("repl", ctx.proc.pid);
17830
- ctx.vfs.writeFile(path, program, { privileged: true, mode: 384 });
17831
- const result = await runQuiet(ctx, path);
17832
- if (result.exitCode !== 0) {
17833
- ctx.vfs.writeFile(path, fallback, { privileged: true, mode: 384 });
17834
- const second = await runQuiet(ctx, path);
17835
- ctx.write(second.stdout);
17836
- if (second.exitCode === 0) statements.push(trimmed);
17837
- else ctx.stderr.write(second.stderr);
17838
- } else {
17839
- ctx.write(result.stdout);
17840
- statements.push(trimmed);
17841
- }
17842
- try {
17843
- ctx.vfs.unlink(path);
17844
- } catch {
17768
+ function collectExportAll(node2) {
17769
+ const id = namespaceFor(node2.source.value);
17770
+ const exported = node2.exported;
17771
+ if (exported) exportGetters.set(nameOf(exported), id);
17772
+ else prelude.push(`${HELPERS.exportAll}(${HELPERS.exports}, ${id});`);
17773
+ drop(node2);
17774
+ }
17775
+ function drop(node2) {
17776
+ edits.push({ start: node2.start, end: node2.end, text: "" });
17777
+ }
17778
+ function rewriteReferences() {
17779
+ const programScope = { names: new Set(importBindings.keys()), parent: null };
17780
+ visit(ast, programScope, true);
17781
+ function visit(node2, scope, isProgram = false) {
17782
+ let childScope = scope;
17783
+ let skip = NOTHING;
17784
+ switch (node2.type) {
17785
+ case "ImportDeclaration":
17786
+ case "ExportAllDeclaration":
17787
+ return;
17788
+ case "ExportNamedDeclaration":
17789
+ if (!node2.declaration) return;
17790
+ break;
17791
+ case "FunctionDeclaration":
17792
+ case "FunctionExpression":
17793
+ case "ArrowFunctionExpression": {
17794
+ const names = /* @__PURE__ */ new Set();
17795
+ for (const param of node2.params ?? []) collectPattern(param, names);
17796
+ const id = node2.id;
17797
+ if (id && node2.type === "FunctionExpression") names.add(id.name);
17798
+ const fnBody = node2.body;
17799
+ if (fnBody?.type === "BlockStatement") {
17800
+ for (const name of hoistedNames(fnBody.body)) names.add(name);
17801
+ }
17802
+ childScope = { names, parent: scope };
17803
+ break;
17804
+ }
17805
+ case "CatchClause": {
17806
+ const names = /* @__PURE__ */ new Set();
17807
+ if (node2.param) collectPattern(node2.param, names);
17808
+ childScope = { names, parent: scope };
17809
+ break;
17810
+ }
17811
+ case "ClassExpression": {
17812
+ const id = node2.id;
17813
+ if (id) childScope = { names: /* @__PURE__ */ new Set([id.name]), parent: scope };
17814
+ break;
17815
+ }
17816
+ case "BlockStatement":
17817
+ case "StaticBlock":
17818
+ if (!isProgram) {
17819
+ childScope = { names: blockNames(node2.body), parent: scope };
17820
+ }
17821
+ break;
17822
+ case "ForStatement":
17823
+ case "ForInStatement":
17824
+ case "ForOfStatement": {
17825
+ const head2 = node2.init ?? node2.left;
17826
+ if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
17827
+ const names = /* @__PURE__ */ new Set();
17828
+ for (const declarator of head2.declarations) {
17829
+ collectPattern(declarator.id, names);
17830
+ }
17831
+ childScope = { names, parent: scope };
17832
+ }
17833
+ break;
17834
+ }
17835
+ case "MetaProperty":
17836
+ edits.push({ start: node2.start, end: node2.end, text: HELPERS.meta });
17837
+ return;
17838
+ case "ImportExpression": {
17839
+ const argument = node2.source;
17840
+ edits.push({ start: node2.start, end: argument.start, text: `${HELPERS.dynamic}(` });
17841
+ visit(argument, childScope);
17842
+ edits.push({ start: argument.end, end: node2.end, text: ")" });
17843
+ return;
17844
+ }
17845
+ case "Identifier": {
17846
+ const replacement = lookup(node2.name, scope);
17847
+ if (replacement) edits.push({ start: node2.start, end: node2.end, text: replacement });
17848
+ return;
17849
+ }
17850
+ case "MemberExpression":
17851
+ case "MethodDefinition":
17852
+ case "PropertyDefinition":
17853
+ skip = node2.computed ? NOTHING : PROPERTY;
17854
+ break;
17855
+ case "Property": {
17856
+ if (node2.computed) break;
17857
+ if (node2.shorthand) {
17858
+ const value = node2.value;
17859
+ if (value.type === "Identifier") {
17860
+ const replacement = lookup(value.name, scope);
17861
+ if (replacement) {
17862
+ edits.push({ start: value.start, end: value.end, text: `${value.name}: ${replacement}` });
17863
+ return;
17864
+ }
17865
+ }
17866
+ break;
17867
+ }
17868
+ skip = PROPERTY;
17869
+ break;
17870
+ }
17871
+ case "LabeledStatement":
17872
+ case "BreakStatement":
17873
+ case "ContinueStatement":
17874
+ skip = LABEL;
17875
+ break;
17876
+ }
17877
+ for (const [key, value] of Object.entries(node2)) {
17878
+ if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
17879
+ if (Array.isArray(value)) {
17880
+ for (const item of value) if (isNode2(item)) visit(item, childScope);
17881
+ } else if (isNode2(value)) {
17882
+ visit(value, childScope);
17883
+ }
17884
+ }
17845
17885
  }
17846
- try {
17847
- ctx.vfs.appendFile(historyPath, trimmed + "\n", { cred: ctx.cred });
17848
- } catch {
17886
+ function lookup(name, scope) {
17887
+ for (let current = scope; current; current = current.parent) {
17888
+ if (current.names.has(name)) {
17889
+ return current.parent === null ? importBindings.get(name) ?? null : null;
17890
+ }
17891
+ }
17892
+ return null;
17849
17893
  }
17850
17894
  }
17851
- return 0;
17852
17895
  }
17853
- async function runQuiet(ctx, script) {
17854
- try {
17855
- const proc = await ctx.kernel.pod.spawn("node", [script], { cwd: ctx.cwd, env: ctx.env });
17856
- return await proc.completion;
17857
- } catch (e) {
17858
- return { exitCode: 1, stdout: "", stderr: `${e instanceof Error ? e.message : String(e)}
17859
- ` };
17896
+ var JSX_EXTENSION = /\.[jt]sx$/;
17897
+ var JsxParser = acorn.Parser.extend(jsx__default.default());
17898
+ var PARSE_OPTIONS = {
17899
+ ecmaVersion: "latest",
17900
+ sourceType: "module",
17901
+ allowAwaitOutsideFunction: true,
17902
+ allowHashBang: true,
17903
+ allowReturnOutsideFunction: true
17904
+ };
17905
+ function parseModule(source, filename) {
17906
+ const parser = JSX_EXTENSION.test(filename) ? JsxParser.parse.bind(JsxParser) : acorn.parse;
17907
+ return parser(source, PARSE_OPTIONS);
17908
+ }
17909
+ function containsImportExpression(node2) {
17910
+ if (Array.isArray(node2)) return node2.some(containsImportExpression);
17911
+ if (!isNode2(node2)) return false;
17912
+ if (node2.type === "ImportExpression") return true;
17913
+ for (const [key, value] of Object.entries(node2)) {
17914
+ if (key === "type" || key === "start" || key === "end") continue;
17915
+ if (containsImportExpression(value)) return true;
17860
17916
  }
17917
+ return false;
17861
17918
  }
17862
- function printHelp(ctx) {
17863
- ctx.line("Usage: node [options] [script.js] [arguments]");
17864
- ctx.line("");
17865
- ctx.line("Options:");
17866
- ctx.line(" -v, --version print Node.js version");
17867
- ctx.line(" -e, --eval script evaluate script");
17868
- ctx.line(" -p, --print evaluate script and print result");
17869
- ctx.line(" -h, --help print this message");
17919
+ function containsImportMeta(node2) {
17920
+ if (Array.isArray(node2)) return node2.some(containsImportMeta);
17921
+ if (!isNode2(node2)) return false;
17922
+ if (node2.type === "MetaProperty") return true;
17923
+ for (const [key, value] of Object.entries(node2)) {
17924
+ if (key === "type" || key === "start" || key === "end") continue;
17925
+ if (containsImportMeta(value)) return true;
17926
+ }
17927
+ return false;
17870
17928
  }
17871
- var nodeVersionFile = defineCommand({
17872
- name: "nvm",
17873
- path: "/usr/local/bin/nvm",
17874
- summary: "node version manager (single-version stub)",
17875
- run(ctx) {
17876
- const sub = ctx.args[0];
17877
- if (sub === "current" || sub === "version") {
17878
- ctx.line(NODE_VERSION);
17879
- return 0;
17929
+ function hasTopLevelAwait(node2) {
17930
+ if (Array.isArray(node2)) return node2.some(hasTopLevelAwait);
17931
+ if (!isNode2(node2)) return false;
17932
+ if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
17933
+ return false;
17934
+ }
17935
+ if (node2.type === "AwaitExpression") return true;
17936
+ if (node2.type === "ForOfStatement" && node2.await === true) return true;
17937
+ for (const [key, value] of Object.entries(node2)) {
17938
+ if (key === "type" || key === "start" || key === "end") continue;
17939
+ if (hasTopLevelAwait(value)) return true;
17940
+ }
17941
+ return false;
17942
+ }
17943
+ function declaredNames(declaration) {
17944
+ const names = /* @__PURE__ */ new Set();
17945
+ if (declaration.type === "VariableDeclaration") {
17946
+ for (const declarator of declaration.declarations) {
17947
+ collectPattern(declarator.id, names);
17880
17948
  }
17881
- if (sub === "ls" || sub === "list") {
17882
- ctx.line(`-> ${NODE_VERSION}`);
17883
- return 0;
17949
+ } else if (isNode2(declaration.id)) {
17950
+ names.add(declaration.id.name);
17951
+ }
17952
+ return [...names];
17953
+ }
17954
+ function nameOf(node2) {
17955
+ return node2.type === "Identifier" ? node2.name : node2.value;
17956
+ }
17957
+ function hoistedNames(body) {
17958
+ const names = blockNames(body);
17959
+ collectVars(body, names);
17960
+ return names;
17961
+ }
17962
+ function blockNames(body) {
17963
+ const names = /* @__PURE__ */ new Set();
17964
+ for (const node2 of body ?? []) {
17965
+ if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
17966
+ for (const declarator of node2.declarations) {
17967
+ collectPattern(declarator.id, names);
17968
+ }
17969
+ } else if ((node2.type === "ClassDeclaration" || node2.type === "FunctionDeclaration") && isNode2(node2.id)) {
17970
+ names.add(node2.id.name);
17884
17971
  }
17885
- ctx.line(`nvm: only ${NODE_VERSION} is available in this container`);
17886
- return 0;
17887
17972
  }
17888
- });
17889
- function nodeCommands() {
17890
- return [node, nodeVersionFile];
17973
+ return names;
17891
17974
  }
17892
-
17893
- // src/python/python.ts
17894
- init_path();
17895
-
17896
- // src/python/host-abi.ts
17897
- var SBX_HOST_ABI_VERSION = 1;
17898
- var SBX_REQUEST_HEADER_BYTES = 16;
17899
- var SBX_RESPONSE_HEADER_BYTES = 20;
17900
- var Op = {
17901
- handshake: 0,
17902
- openat: 1,
17903
- close: 2,
17904
- read: 3,
17905
- write: 4,
17906
- pread: 5,
17907
- pwrite: 6,
17908
- lseek: 7,
17909
- fstat: 8,
17910
- statat: 9,
17911
- ftruncate: 10,
17912
- renameat: 11,
17913
- unlinkat: 12,
17914
- mkdirat: 13,
17915
- readlinkat: 14,
17916
- symlinkat: 15,
17917
- getdents: 16,
17918
- fsync: 17,
17919
- dup: 256,
17920
- dup2: 257,
17921
- get_flags: 258,
17922
- set_flags: 259,
17923
- pipe: 512,
17924
- poll: 768,
17925
- clock_gettime: 1024,
17926
- sleep: 1025,
17927
- getpid: 1280,
17928
- getcwd: 1281,
17975
+ function collectVars(nodes, names) {
17976
+ if (Array.isArray(nodes)) {
17977
+ for (const item of nodes) collectVars(item, names);
17978
+ return;
17979
+ }
17980
+ if (!isNode2(nodes)) return;
17981
+ const node2 = nodes;
17982
+ if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
17983
+ if (isNode2(node2.id)) names.add(node2.id.name);
17984
+ return;
17985
+ }
17986
+ if (node2.type === "VariableDeclaration" && node2.kind === "var") {
17987
+ for (const declarator of node2.declarations) {
17988
+ collectPattern(declarator.id, names);
17989
+ }
17990
+ }
17991
+ for (const [key, value] of Object.entries(node2)) {
17992
+ if (key === "type" || key === "start" || key === "end") continue;
17993
+ collectVars(value, names);
17994
+ }
17995
+ }
17996
+ function collectPattern(node2, names) {
17997
+ if (!isNode2(node2)) return;
17998
+ switch (node2.type) {
17999
+ case "Identifier":
18000
+ names.add(node2.name);
18001
+ return;
18002
+ case "ObjectPattern":
18003
+ for (const property of node2.properties) {
18004
+ collectPattern(property.value ?? property.argument, names);
18005
+ }
18006
+ return;
18007
+ case "ArrayPattern":
18008
+ for (const element of node2.elements) collectPattern(element, names);
18009
+ return;
18010
+ case "AssignmentPattern":
18011
+ collectPattern(node2.left, names);
18012
+ return;
18013
+ case "RestElement":
18014
+ collectPattern(node2.argument, names);
18015
+ return;
18016
+ default:
18017
+ return;
18018
+ }
18019
+ }
18020
+ function applyEdits(source, edits) {
18021
+ const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
18022
+ let out = source;
18023
+ for (const edit of ordered) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
18024
+ return out;
18025
+ }
18026
+ function isNode2(value) {
18027
+ return typeof value === "object" && value !== null && typeof value.type === "string";
18028
+ }
18029
+ var NOTHING = [];
18030
+ var PROPERTY = ["property", "key"];
18031
+ var LABEL = ["label"];
18032
+
18033
+ // src/node/node.ts
18034
+ var NODE_VERSION = "v22.12.0";
18035
+ new TextEncoder();
18036
+ var tempCounter = 0;
18037
+ function tempPath(kind, pid, ext = ".js") {
18038
+ return `/tmp/.sbx-${kind}-${pid}-${tempCounter++}${ext}`;
18039
+ }
18040
+ function withPrelude(source, prelude) {
18041
+ if (!source.startsWith("#!")) return prelude + source;
18042
+ const eol = source.indexOf("\n");
18043
+ if (eol < 0) return source + "\n" + prelude;
18044
+ return source.slice(0, eol + 1) + prelude + source.slice(eol + 1);
18045
+ }
18046
+ function siblingPath(script, pid) {
18047
+ const ext = extname(script) || ".js";
18048
+ return join(dirname(script), `.sbx-${pid}-${tempCounter++}-${basename(script, ext)}${ext}`);
18049
+ }
18050
+ var BUFFER_COMPAT_SHIM = `
18051
+ (function () {
18052
+ const seen = new Set();
18053
+ const widen = (B) => {
18054
+ if (!B || seen.has(B) || typeof B.isBuffer !== 'function') return;
18055
+ seen.add(B);
18056
+ const original = B.isBuffer.bind(B);
18057
+ B.isBuffer = (value) => original(value) || value instanceof Uint8Array;
18058
+ };
18059
+ try { widen(globalThis.Buffer); } catch (e) {}
18060
+ try { widen(require('buffer').Buffer); } catch (e) {}
18061
+ })();
18062
+ `;
18063
+ function buildPrelude(scriptPath, argv, stdinPath, preloads = []) {
18064
+ const argvLiteral = JSON.stringify(["/usr/bin/node", scriptPath, ...argv]);
18065
+ const pathLiteral = JSON.stringify(scriptPath);
18066
+ const stdinLiteral = JSON.stringify(stdinPath);
18067
+ return `${BUFFER_COMPAT_SHIM}
18068
+ process.argv = ${argvLiteral};
18069
+ try {
18070
+ __filename = ${pathLiteral};
18071
+ module.filename = ${pathLiteral};
18072
+ module.id = ${pathLiteral};
18073
+ if (require.main === module) require.main.filename = ${pathLiteral};
18074
+ } catch (e) {}
18075
+ ${stdinPath === null ? "" : `(function () {
18076
+ const fs = require('fs');
18077
+ const { Readable } = require('stream');
18078
+ let data;
18079
+ try { data = fs.readFileSync(${stdinLiteral}); } catch (e) { data = Buffer.alloc(0); }
18080
+ const stream = Readable.from([data]);
18081
+ stream.isTTY = false;
18082
+ stream.fd = 0;
18083
+ stream.setRawMode = function () { return this; };
18084
+ try {
18085
+ Object.defineProperty(process, 'stdin', { value: stream, configurable: true, writable: true });
18086
+ } catch (e) { process.stdin = stream; }
18087
+ })();
18088
+ `}${preloadRequires(preloads)}`;
18089
+ }
18090
+ function preloadRequires(preloads) {
18091
+ return preloads.map((specifier) => `require(${JSON.stringify(specifier)});
18092
+ `).join("");
18093
+ }
18094
+ async function execute(ctx, invocation) {
18095
+ const { pod } = ctx.kernel;
18096
+ const live = ctx.stdin.interactive || ctx.stdin.isTTY;
18097
+ const tty = ctx.stdin.isTTY;
18098
+ let exitCode = 0;
18099
+ try {
18100
+ const proc = await pod.spawn("node", [invocation.script], {
18101
+ cwd: ctx.cwd,
18102
+ env: { ...invocation.env ?? ctx.env, PWD: ctx.cwd },
18103
+ argv: ["/usr/bin/node", invocation.argvPath ?? invocation.script, ...invocation.argv],
18104
+ ...invocation.stdinPath ? { stdinPath: invocation.stdinPath } : {},
18105
+ /* An interactive caller's input was not captured to a file, so the
18106
+ * program's stdin has to stay open and be fed as it arrives — and be
18107
+ * reported as a terminal, since that is what decides whether a CLI
18108
+ * prompts or takes its defaults. */
18109
+ ...live ? { interactiveStdin: true } : {},
18110
+ ...tty ? { tty: true } : {}
18111
+ });
18112
+ proc.on("output", (chunk) => {
18113
+ try {
18114
+ ctx.write(chunk);
18115
+ } catch {
18116
+ }
18117
+ });
18118
+ proc.on("error", (chunk) => {
18119
+ try {
18120
+ ctx.stderr.write(chunk);
18121
+ } catch {
18122
+ }
18123
+ });
18124
+ proc.on("rawmode", (enabled) => {
18125
+ ctx.stdin.rawMode = enabled;
18126
+ });
18127
+ let forwarding = false;
18128
+ if (live) {
18129
+ forwarding = true;
18130
+ void (async () => {
18131
+ while (forwarding) {
18132
+ const chunk = await ctx.stdin.read();
18133
+ if (chunk === null) {
18134
+ proc.endInput?.();
18135
+ break;
18136
+ }
18137
+ try {
18138
+ proc.write(new TextDecoder().decode(chunk));
18139
+ } catch {
18140
+ break;
18141
+ }
18142
+ }
18143
+ })();
18144
+ }
18145
+ let onAbort;
18146
+ const aborted = new Promise((resolve3) => {
18147
+ onAbort = () => {
18148
+ try {
18149
+ proc.kill();
18150
+ } catch {
18151
+ }
18152
+ resolve3({ exitCode: 137 });
18153
+ };
18154
+ if (ctx.signal.aborted) onAbort();
18155
+ else ctx.signal.addEventListener("abort", onAbort, { once: true });
18156
+ });
18157
+ const result = await Promise.race([proc.completion, aborted]);
18158
+ forwarding = false;
18159
+ if (onAbort) ctx.signal.removeEventListener("abort", onAbort);
18160
+ exitCode = result.exitCode;
18161
+ } catch (e) {
18162
+ ctx.stderr.write(`node: ${e instanceof Error ? e.message : String(e)}
18163
+ `);
18164
+ exitCode = 1;
18165
+ } finally {
18166
+ for (const temp of invocation.temps) {
18167
+ try {
18168
+ ctx.vfs.unlink(temp);
18169
+ } catch {
18170
+ }
18171
+ }
18172
+ }
18173
+ return exitCode;
18174
+ }
18175
+ async function captureStdin(ctx) {
18176
+ if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
18177
+ const bytes2 = await ctx.stdin.readAll();
18178
+ if (bytes2.length === 0) return null;
18179
+ const path = tempPath("stdin", ctx.proc.pid, ".dat");
18180
+ ctx.vfs.writeFile(path, bytes2, { privileged: true, mode: 384 });
18181
+ return path;
18182
+ }
18183
+ var node = defineCommand({
18184
+ name: "node",
18185
+ path: "/usr/bin/node",
18186
+ aliases: ["nodejs"],
18187
+ summary: "run a JavaScript program with Node.js",
18188
+ usage: "node [options] [script.js] [arguments]",
18189
+ manual: `Runs JavaScript with a real Node.js API surface \u2014 fs, path, http,
18190
+ crypto, streams, worker_threads and npm packages \u2014 inside the container.
18191
+ The filesystem a script sees is the container's filesystem.`,
18192
+ async run(ctx) {
18193
+ const argv = ctx.args;
18194
+ const temps = [];
18195
+ let i = 0;
18196
+ let evalSource;
18197
+ let printResult = false;
18198
+ let scriptArg;
18199
+ const preloads = [];
18200
+ const envFiles = [];
18201
+ let testMode = false;
18202
+ let checkOnly = false;
18203
+ const testSettings = { namePatterns: [] };
18204
+ for (; i < argv.length; i++) {
18205
+ const arg = argv[i];
18206
+ if (arg === "-v" || arg === "--version") {
18207
+ ctx.line(NODE_VERSION);
18208
+ return 0;
18209
+ }
18210
+ if (arg === "-h" || arg === "--help") {
18211
+ printHelp(ctx);
18212
+ return 0;
18213
+ }
18214
+ if (arg === "-e" || arg === "--eval") {
18215
+ evalSource = argv[++i] ?? "";
18216
+ i++;
18217
+ break;
18218
+ }
18219
+ if (arg === "-p" || arg === "--print") {
18220
+ printResult = true;
18221
+ evalSource = argv[++i] ?? "";
18222
+ i++;
18223
+ break;
18224
+ }
18225
+ if (arg.startsWith("--eval=")) {
18226
+ evalSource = arg.slice("--eval=".length);
18227
+ i++;
18228
+ break;
18229
+ }
18230
+ if (arg.startsWith("--print=")) {
18231
+ printResult = true;
18232
+ evalSource = arg.slice("--print=".length);
18233
+ i++;
18234
+ break;
18235
+ }
18236
+ if (arg === "--") {
18237
+ i++;
18238
+ break;
18239
+ }
18240
+ const flag = arg.startsWith("--") ? arg.split("=", 1)[0] : arg;
18241
+ if (VALUE_FLAGS.has(flag)) {
18242
+ const value = arg.length > flag.length ? arg.slice(flag.length + 1) : argv[++i];
18243
+ if (value === void 0) {
18244
+ ctx.stderr.write(`node: ${flag} requires an argument
18245
+ `);
18246
+ return 9;
18247
+ }
18248
+ if (flag === "-r" || flag === "--require" || flag === "--import") preloads.push(value);
18249
+ else if (flag === "--env-file") envFiles.push({ path: value, optional: false });
18250
+ else if (flag === "--env-file-if-exists") envFiles.push({ path: value, optional: true });
18251
+ else if (flag === "--test-reporter") testSettings.reporter = value === "tap" ? "tap" : "spec";
18252
+ else if (flag === "--test-name-pattern") testSettings.namePatterns.push(value);
18253
+ else if (flag === "--loader" || flag === "--experimental-loader") {
18254
+ ctx.stderr.write(`node: ${flag} is not supported in this container; ${value} was not loaded
18255
+ `);
18256
+ }
18257
+ continue;
18258
+ }
18259
+ if (arg === "--test") {
18260
+ testMode = true;
18261
+ continue;
18262
+ }
18263
+ if (arg === "--test-only") {
18264
+ testSettings.only = true;
18265
+ continue;
18266
+ }
18267
+ if (arg === "-c" || arg === "--check") {
18268
+ checkOnly = true;
18269
+ continue;
18270
+ }
18271
+ if (flag === "--watch" || flag === "--watch-path" || flag === "--watch-preserve-output") {
18272
+ ctx.stderr.write(
18273
+ `node: ${flag} is not available in this container: file changes are not watched. Run the program again after changing it.
18274
+ `
18275
+ );
18276
+ return 9;
18277
+ }
18278
+ if (arg.startsWith("-") && arg !== "-") {
18279
+ continue;
18280
+ }
18281
+ scriptArg = arg;
18282
+ i++;
18283
+ break;
18284
+ }
18285
+ const scriptArgs = argv.slice(i);
18286
+ let env2;
18287
+ if (envFiles.length > 0) {
18288
+ const loaded = {};
18289
+ for (const file3 of envFiles) {
18290
+ let text2;
18291
+ try {
18292
+ text2 = ctx.vfs.readText(ctx.path(file3.path), ctx.cred);
18293
+ } catch {
18294
+ if (file3.optional) continue;
18295
+ ctx.stderr.write(`node: ${file3.path}: not found
18296
+ `);
18297
+ return 9;
18298
+ }
18299
+ Object.assign(loaded, parseEnvFile(text2));
18300
+ }
18301
+ env2 = { ...loaded, ...ctx.env };
18302
+ }
18303
+ const preloadSpecs = preloads.map((spec) => spec.startsWith(".") || spec.startsWith("/") ? ctx.path(spec) : spec);
18304
+ if (testMode) {
18305
+ return runTestFiles(ctx, scriptArg === void 0 ? scriptArgs : [scriptArg, ...scriptArgs], testSettings, preloads, env2 ?? ctx.env);
18306
+ }
18307
+ if (checkOnly && scriptArg !== void 0) {
18308
+ const script2 = resolveScript(ctx, scriptArg);
18309
+ if (!script2) {
18310
+ ctx.stderr.write(`node: cannot find module '${ctx.path(scriptArg)}'
18311
+ `);
18312
+ return 1;
18313
+ }
18314
+ return checkSyntax(ctx, script2);
18315
+ }
18316
+ if (evalSource !== void 0) {
18317
+ const body = printResult ? `const __sbxResult = (function(){ return (${evalSource}
18318
+ ); })();
18319
+ console.log(typeof __sbxResult === 'string' ? __sbxResult : require('util').inspect(__sbxResult));` : evalSource;
18320
+ const path = tempPath("eval", ctx.proc.pid);
18321
+ const stdinPath2 = await captureStdin(ctx);
18322
+ if (stdinPath2) temps.push(stdinPath2);
18323
+ ctx.vfs.writeFile(path, buildBootstrapForEval(body, ["/usr/bin/node", ...scriptArgs], stdinPath2, preloadSpecs), {
18324
+ privileged: true,
18325
+ mode: 384
18326
+ });
18327
+ temps.push(path);
18328
+ return execute(ctx, { script: path, argv: scriptArgs, stdinPath: stdinPath2, temps, ...env2 ? { env: env2 } : {} });
18329
+ }
18330
+ if (scriptArg === void 0 || scriptArg === "-") {
18331
+ if (ctx.stdin.isTTY) {
18332
+ ctx.line(`Welcome to Node.js ${NODE_VERSION}.`);
18333
+ ctx.line('Type ".help" for more information.');
18334
+ return runRepl(ctx);
18335
+ }
18336
+ const source = new TextDecoder().decode(await ctx.stdin.readAll());
18337
+ if (source.trim() === "") return 0;
18338
+ const path = tempPath("stdin-script", ctx.proc.pid);
18339
+ ctx.vfs.writeFile(path, buildBootstrapForEval(source, ["/usr/bin/node", ...scriptArgs], null, preloadSpecs), {
18340
+ privileged: true,
18341
+ mode: 384
18342
+ });
18343
+ temps.push(path);
18344
+ return execute(ctx, { script: path, argv: scriptArgs, temps, ...env2 ? { env: env2 } : {} });
18345
+ }
18346
+ const script = resolveScript(ctx, scriptArg);
18347
+ if (!script) {
18348
+ ctx.stderr.write(
18349
+ `node:internal/modules/cjs/loader:1215
18350
+ throw err;
18351
+ ^
18352
+
18353
+ Error: Cannot find module '${ctx.path(scriptArg)}'
18354
+ `
18355
+ );
18356
+ return 1;
18357
+ }
18358
+ const stdinPath = await captureStdin(ctx);
18359
+ if (stdinPath) temps.push(stdinPath);
18360
+ const sibling = siblingPath(script, ctx.proc.pid);
18361
+ try {
18362
+ const source = ctx.vfs.readText(script, ctx.cred);
18363
+ ctx.vfs.writeFile(sibling, withPrelude(source, buildPrelude(script, scriptArgs, stdinPath, preloadSpecs)), {
18364
+ privileged: true,
18365
+ mode: 384
18366
+ });
18367
+ temps.push(sibling);
18368
+ return await execute(ctx, {
18369
+ script: sibling,
18370
+ argvPath: script,
18371
+ argv: scriptArgs,
18372
+ stdinPath,
18373
+ temps,
18374
+ ...env2 ? { env: env2 } : {}
18375
+ });
18376
+ } catch {
18377
+ return await execute(ctx, { script, argv: scriptArgs, stdinPath, temps, ...env2 ? { env: env2 } : {} });
18378
+ }
18379
+ }
18380
+ });
18381
+ function buildBootstrapForEval(source, argv, stdinPath, preloads = []) {
18382
+ const prelude = stdinPath ? `const fs = require('fs');
18383
+ const { Readable } = require('stream');
18384
+ let __sbxData; try { __sbxData = fs.readFileSync(${JSON.stringify(stdinPath)}); } catch (e) { __sbxData = Buffer.alloc(0); }
18385
+ const __sbxStdin = Readable.from([__sbxData]);
18386
+ __sbxStdin.isTTY = false;
18387
+ __sbxStdin.setRawMode = function () { return this; };
18388
+ try { Object.defineProperty(process, 'stdin', { value: __sbxStdin, configurable: true, writable: true }); } catch (e) { process.stdin = __sbxStdin; }
18389
+ ` : "";
18390
+ return `${BUFFER_COMPAT_SHIM}process.argv = ${JSON.stringify(argv)};
18391
+ ${prelude}${preloadRequires(preloads)}${source}
18392
+ `;
18393
+ }
18394
+ function resolveScript(ctx, operand) {
18395
+ const base2 = ctx.path(operand);
18396
+ const candidates = [base2, `${base2}.js`, `${base2}.mjs`, `${base2}.cjs`, `${base2}.json`];
18397
+ for (const candidate of candidates) {
18398
+ try {
18399
+ const st = ctx.vfs.stat(candidate, { cred: ctx.cred });
18400
+ if (st.isFile()) return ctx.vfs.realpath(candidate, ctx.cred);
18401
+ if (st.isDirectory()) {
18402
+ const manifest = join(candidate, "package.json");
18403
+ if (ctx.vfs.lexists(manifest)) {
18404
+ try {
18405
+ const main = JSON.parse(ctx.vfs.readText(manifest, ctx.cred)).main;
18406
+ if (main) {
18407
+ const mainPath = resolve(candidate, main);
18408
+ if (ctx.vfs.lexists(mainPath)) return ctx.vfs.realpath(mainPath, ctx.cred);
18409
+ if (ctx.vfs.lexists(mainPath + ".js")) return ctx.vfs.realpath(mainPath + ".js", ctx.cred);
18410
+ }
18411
+ } catch {
18412
+ }
18413
+ }
18414
+ for (const index of ["index.js", "index.mjs", "index.cjs"]) {
18415
+ const path = join(candidate, index);
18416
+ if (ctx.vfs.lexists(path)) return ctx.vfs.realpath(path, ctx.cred);
18417
+ }
18418
+ }
18419
+ } catch {
18420
+ }
18421
+ }
18422
+ return null;
18423
+ }
18424
+ async function runRepl(ctx) {
18425
+ const historyPath = join(ctx.env.HOME ?? "/root", ".node_repl_history");
18426
+ const statements = [];
18427
+ for (; ; ) {
18428
+ ctx.write("> ");
18429
+ const line = await ctx.stdin.readLine();
18430
+ if (line === null) {
18431
+ ctx.line("");
18432
+ break;
18433
+ }
18434
+ const trimmed = line.trim();
18435
+ if (trimmed === ".exit") break;
18436
+ if (trimmed === ".help") {
18437
+ ctx.line(".exit Exit the REPL");
18438
+ ctx.line(".help Print this help message");
18439
+ ctx.line(".clear Reset the session context");
18440
+ continue;
18441
+ }
18442
+ if (trimmed === ".clear") {
18443
+ statements.length = 0;
18444
+ continue;
18445
+ }
18446
+ if (trimmed === "") continue;
18447
+ const program = [...statements, `console.log(require('util').inspect((${trimmed})))`].join(";\n");
18448
+ const fallback = [...statements, trimmed].join(";\n");
18449
+ const path = tempPath("repl", ctx.proc.pid);
18450
+ ctx.vfs.writeFile(path, program, { privileged: true, mode: 384 });
18451
+ const result = await runQuiet(ctx, path);
18452
+ if (result.exitCode !== 0) {
18453
+ ctx.vfs.writeFile(path, fallback, { privileged: true, mode: 384 });
18454
+ const second = await runQuiet(ctx, path);
18455
+ ctx.write(second.stdout);
18456
+ if (second.exitCode === 0) statements.push(trimmed);
18457
+ else ctx.stderr.write(second.stderr);
18458
+ } else {
18459
+ ctx.write(result.stdout);
18460
+ statements.push(trimmed);
18461
+ }
18462
+ try {
18463
+ ctx.vfs.unlink(path);
18464
+ } catch {
18465
+ }
18466
+ try {
18467
+ ctx.vfs.appendFile(historyPath, trimmed + "\n", { cred: ctx.cred });
18468
+ } catch {
18469
+ }
18470
+ }
18471
+ return 0;
18472
+ }
18473
+ async function runQuiet(ctx, script) {
18474
+ try {
18475
+ const proc = await ctx.kernel.pod.spawn("node", [script], { cwd: ctx.cwd, env: ctx.env });
18476
+ return await proc.completion;
18477
+ } catch (e) {
18478
+ return { exitCode: 1, stdout: "", stderr: `${e instanceof Error ? e.message : String(e)}
18479
+ ` };
18480
+ }
18481
+ }
18482
+ function printHelp(ctx) {
18483
+ ctx.line("Usage: node [options] [script.js] [arguments]");
18484
+ ctx.line(" node --test [files or globs]");
18485
+ ctx.line("");
18486
+ ctx.line("Options:");
18487
+ ctx.line(" -v, --version print Node.js version");
18488
+ ctx.line(" -e, --eval script evaluate script");
18489
+ ctx.line(" -p, --print evaluate script and print result");
18490
+ ctx.line(" -c, --check check syntax without running");
18491
+ ctx.line(" -r, --require module load a module before the program");
18492
+ ctx.line(" --import module load a module before the program");
18493
+ ctx.line(" --env-file file load environment variables from a file");
18494
+ ctx.line(" --test run test files with node:test");
18495
+ ctx.line(" --test-reporter spec|tap choose the test report format");
18496
+ ctx.line(" --test-name-pattern regex run only tests whose name matches");
18497
+ ctx.line(" -h, --help print this message");
18498
+ }
18499
+ var VALUE_FLAGS = /* @__PURE__ */ new Set([
18500
+ "-r",
18501
+ "--require",
18502
+ "--import",
18503
+ "--loader",
18504
+ "--experimental-loader",
18505
+ "--env-file",
18506
+ "--env-file-if-exists",
18507
+ "--input-type",
18508
+ "--title",
18509
+ "-C",
18510
+ "--conditions",
18511
+ "--test-reporter",
18512
+ "--test-reporter-destination",
18513
+ "--test-name-pattern",
18514
+ "--test-skip-pattern",
18515
+ "--test-concurrency",
18516
+ "--test-timeout",
18517
+ "--test-shard",
18518
+ "--disable-warning",
18519
+ "--redirect-warnings",
18520
+ "--stack-trace-limit",
18521
+ "--inspect-port",
18522
+ "--report-dir",
18523
+ "--report-filename"
18524
+ ]);
18525
+ function parseEnvFile(text2) {
18526
+ const out = {};
18527
+ for (const raw of text2.split(/\r?\n/)) {
18528
+ const line = raw.trim();
18529
+ if (!line || line.startsWith("#")) continue;
18530
+ const match2 = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line);
18531
+ if (!match2) continue;
18532
+ let value = match2[2];
18533
+ const quote3 = value[0];
18534
+ if ((quote3 === '"' || quote3 === "'" || quote3 === "`") && value.length >= 2 && value.endsWith(quote3)) {
18535
+ value = value.slice(1, -1);
18536
+ if (quote3 === '"') value = value.replace(/\\n/g, "\n");
18537
+ } else {
18538
+ const comment = value.indexOf(" #");
18539
+ if (comment >= 0) value = value.slice(0, comment);
18540
+ value = value.trim();
18541
+ }
18542
+ out[match2[1]] = value;
18543
+ }
18544
+ return out;
18545
+ }
18546
+ var AsyncFunction = Object.getPrototypeOf(async function() {
18547
+ }).constructor;
18548
+ function checkSyntax(ctx, script) {
18549
+ let source = ctx.vfs.readText(script, ctx.cred);
18550
+ if (source.startsWith("#!")) source = `//${source.slice(2)}`;
18551
+ try {
18552
+ const esm = script.endsWith(".mjs") || !script.endsWith(".cjs") && looksLikeEsm(source);
18553
+ const transformed = esm ? transformEsm(source, script) : null;
18554
+ new AsyncFunction("exports", "require", "module", "__filename", "__dirname", transformed?.code ?? source);
18555
+ return 0;
18556
+ } catch (error) {
18557
+ if (!(error instanceof SyntaxError)) throw error;
18558
+ ctx.stderr.write(`${script}
18559
+
18560
+ ${error.name}: ${error.message}
18561
+
18562
+ Node.js ${NODE_VERSION}
18563
+ `);
18564
+ return 1;
18565
+ }
18566
+ }
18567
+ var TEST_FILE_NAME = /^(?:test|test-.+|.+[.\-_]test)\.(?:c|m)?js$/;
18568
+ var SCRIPT_FILE = /\.(?:c|m)?js$/;
18569
+ function globToRegExp(pattern) {
18570
+ let out = "";
18571
+ for (let index = 0; index < pattern.length; index += 1) {
18572
+ const ch = pattern[index];
18573
+ if (ch === "*") {
18574
+ if (pattern[index + 1] === "*") {
18575
+ index += 1;
18576
+ if (pattern[index + 1] === "/") index += 1;
18577
+ out += "(?:.*/)?";
18578
+ } else out += "[^/]*";
18579
+ } else if (ch === "?") out += "[^/]";
18580
+ else out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
18581
+ }
18582
+ return new RegExp(`^${out}$`);
18583
+ }
18584
+ function discoverTestFiles(ctx, patterns) {
18585
+ const files = /* @__PURE__ */ new Set();
18586
+ const missing = [];
18587
+ const walk = (dir3, visit, underTestDir = false) => {
18588
+ let names;
18589
+ try {
18590
+ names = ctx.vfs.readdir(dir3, ctx.cred);
18591
+ } catch {
18592
+ return;
18593
+ }
18594
+ for (const name of [...names].sort()) {
18595
+ if (name === "node_modules" || name.startsWith(".")) continue;
18596
+ const path = join(dir3, name);
18597
+ let isDirectory;
18598
+ try {
18599
+ isDirectory = ctx.vfs.stat(path, { cred: ctx.cred }).isDirectory();
18600
+ } catch {
18601
+ continue;
18602
+ }
18603
+ if (isDirectory) walk(path, visit, underTestDir || name === "test");
18604
+ else visit(path, name, underTestDir);
18605
+ }
18606
+ };
18607
+ const byName = (path, name, underTestDir) => {
18608
+ if (SCRIPT_FILE.test(name) && (underTestDir || TEST_FILE_NAME.test(name))) files.add(path);
18609
+ };
18610
+ if (patterns.length === 0) walk(ctx.cwd, byName);
18611
+ for (const pattern of patterns) {
18612
+ const target = ctx.path(pattern);
18613
+ let stat2 = null;
18614
+ try {
18615
+ stat2 = ctx.vfs.stat(target, { cred: ctx.cred });
18616
+ } catch {
18617
+ stat2 = null;
18618
+ }
18619
+ if (stat2?.isFile()) {
18620
+ files.add(target);
18621
+ continue;
18622
+ }
18623
+ if (stat2?.isDirectory()) {
18624
+ walk(target, byName, basename(target) === "test");
18625
+ continue;
18626
+ }
18627
+ if (/[*?]/.test(pattern)) {
18628
+ const matcher = globToRegExp(target);
18629
+ const before = files.size;
18630
+ walk(ctx.cwd, (path) => {
18631
+ if (matcher.test(path)) files.add(path);
18632
+ });
18633
+ if (files.size > before) continue;
18634
+ }
18635
+ missing.push(pattern);
18636
+ }
18637
+ return { files: [...files].sort(), missing };
18638
+ }
18639
+ async function runTestFiles(ctx, args, settings, preloads, env2) {
18640
+ const patterns = [];
18641
+ for (const arg of args) {
18642
+ if (arg.startsWith("--test-reporter=")) settings.reporter = arg.endsWith("=tap") ? "tap" : "spec";
18643
+ else if (arg.startsWith("--test-name-pattern=")) settings.namePatterns.push(arg.slice("--test-name-pattern=".length));
18644
+ else if (arg === "--test-only") settings.only = true;
18645
+ else if (!arg.startsWith("-")) patterns.push(arg);
18646
+ }
18647
+ const { files, missing } = discoverTestFiles(ctx, patterns);
18648
+ if (missing.length > 0) {
18649
+ for (const pattern of missing) ctx.stderr.write(`Could not find '${ctx.path(pattern)}'
18650
+ `);
18651
+ return 1;
18652
+ }
18653
+ if (files.length === 0) {
18654
+ ctx.stderr.write(
18655
+ "node --test: no test files found. Test files are named *.test.js, *-test.js, *_test.js, test-*.js or test.js (also .mjs and .cjs), or live in a test/ directory; node_modules is not searched.\n"
18656
+ );
18657
+ return 0;
18658
+ }
18659
+ const childEnv = { ...env2, SANDBOXEDJS_NODE_TEST: JSON.stringify(settings) };
18660
+ let failed = 0;
18661
+ for (const file3 of files) {
18662
+ const child = ctx.kernel.spawn(["node", ...preloads.flatMap((spec) => ["-r", spec]), file3], {
18663
+ cwd: ctx.cwd,
18664
+ env: childEnv,
18665
+ cred: ctx.cred,
18666
+ ppid: ctx.proc.pid,
18667
+ stdout: ctx.stdout,
18668
+ stderr: ctx.stderr
18669
+ });
18670
+ if (await child.wait() !== 0) failed += 1;
18671
+ }
18672
+ if (files.length > 1) {
18673
+ ctx.line(settings.reporter === "tap" ? `# files ${files.length} failed ${failed}` : `\u2139 files ${files.length}, failed ${failed}`);
18674
+ }
18675
+ return failed > 0 ? 1 : 0;
18676
+ }
18677
+ var nodeVersionFile = defineCommand({
18678
+ name: "nvm",
18679
+ path: "/usr/local/bin/nvm",
18680
+ summary: "node version manager (single-version stub)",
18681
+ run(ctx) {
18682
+ const sub = ctx.args[0];
18683
+ if (sub === "current" || sub === "version") {
18684
+ ctx.line(NODE_VERSION);
18685
+ return 0;
18686
+ }
18687
+ if (sub === "ls" || sub === "list") {
18688
+ ctx.line(`-> ${NODE_VERSION}`);
18689
+ return 0;
18690
+ }
18691
+ ctx.line(`nvm: only ${NODE_VERSION} is available in this container`);
18692
+ return 0;
18693
+ }
18694
+ });
18695
+ function nodeCommands() {
18696
+ return [node, nodeVersionFile];
18697
+ }
18698
+
18699
+ // src/python/python.ts
18700
+ init_path();
18701
+
18702
+ // src/python/host-abi.ts
18703
+ var SBX_HOST_ABI_VERSION = 1;
18704
+ var SBX_REQUEST_HEADER_BYTES = 16;
18705
+ var SBX_RESPONSE_HEADER_BYTES = 20;
18706
+ var Op = {
18707
+ handshake: 0,
18708
+ openat: 1,
18709
+ close: 2,
18710
+ read: 3,
18711
+ write: 4,
18712
+ pread: 5,
18713
+ pwrite: 6,
18714
+ lseek: 7,
18715
+ fstat: 8,
18716
+ statat: 9,
18717
+ ftruncate: 10,
18718
+ renameat: 11,
18719
+ unlinkat: 12,
18720
+ mkdirat: 13,
18721
+ readlinkat: 14,
18722
+ symlinkat: 15,
18723
+ getdents: 16,
18724
+ fsync: 17,
18725
+ dup: 256,
18726
+ dup2: 257,
18727
+ get_flags: 258,
18728
+ set_flags: 259,
18729
+ pipe: 512,
18730
+ poll: 768,
18731
+ clock_gettime: 1024,
18732
+ sleep: 1025,
18733
+ getpid: 1280,
18734
+ getcwd: 1281,
17929
18735
  chdir: 1282,
17930
18736
  environ: 1283,
17931
18737
  getrandom: 1536,
@@ -18363,7 +19169,7 @@ var wheels_default = {
18363
19169
 
18364
19170
  // package.json
18365
19171
  var package_default = {
18366
- version: "0.1.73"};
19172
+ version: "0.1.74"};
18367
19173
 
18368
19174
  // src/python/config.ts
18369
19175
  function runtimeModuleUrl() {
@@ -22334,7 +23140,7 @@ var pyodideModule = null;
22334
23140
  var indexUrl;
22335
23141
  var moduleUrl;
22336
23142
  var DEFAULT_BROWSER_INDEX_URL = "https://cdn.jsdelivr.net/pyodide/v0.28.3/full/";
22337
- var isNode2 = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
23143
+ var isNode3 = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
22338
23144
  function configureCPython(options = {}) {
22339
23145
  if (options.indexURL !== void 0) indexUrl = options.indexURL;
22340
23146
  if (options.moduleURL !== void 0) {
@@ -22350,7 +23156,7 @@ function importPyodide() {
22350
23156
  /* webpackIgnore: true */
22351
23157
  moduleUrl
22352
23158
  );
22353
- } else if (isNode2) {
23159
+ } else if (isNode3) {
22354
23160
  pyodideModule = nodeOnlyModule("pyodide");
22355
23161
  } else {
22356
23162
  pyodideModule = import(
@@ -22364,7 +23170,7 @@ function importPyodide() {
22364
23170
  }
22365
23171
  async function resolveIndexUrl() {
22366
23172
  if (indexUrl) return indexUrl;
22367
- if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
23173
+ if (!isNode3) return DEFAULT_BROWSER_INDEX_URL;
22368
23174
  try {
22369
23175
  const { createRequire } = await nodeBuiltin("module");
22370
23176
  const path = await nodeBuiltin("path");
@@ -24303,7 +25109,7 @@ function renameShadowedExports(source) {
24303
25109
  (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
24304
25110
  );
24305
25111
  if (!isModule) return null;
24306
- const programScope = { bindsExports: hoistedNames(body).has(NAME), parent: null };
25112
+ const programScope = { bindsExports: hoistedNames2(body).has(NAME), parent: null };
24307
25113
  if (!programScope.bindsExports) return null;
24308
25114
  const targets = [];
24309
25115
  let bail = false;
@@ -24319,24 +25125,24 @@ function renameShadowedExports(source) {
24319
25125
  function visit(node2, scope) {
24320
25126
  if (bail) return;
24321
25127
  let childScope = scope;
24322
- let skip = NOTHING;
25128
+ let skip = NOTHING2;
24323
25129
  switch (node2.type) {
24324
25130
  case "FunctionDeclaration":
24325
25131
  case "FunctionExpression":
24326
25132
  case "ArrowFunctionExpression": {
24327
25133
  const names = /* @__PURE__ */ new Set();
24328
- for (const param of node2.params ?? []) collectPattern(param, names);
25134
+ for (const param of node2.params ?? []) collectPattern2(param, names);
24329
25135
  if (isExports(node2.id) && node2.type === "FunctionExpression") names.add(NAME);
24330
25136
  const fnBody = node2.body;
24331
25137
  if (fnBody?.type === "BlockStatement") {
24332
- for (const name of hoistedNames(fnBody.body)) names.add(name);
25138
+ for (const name of hoistedNames2(fnBody.body)) names.add(name);
24333
25139
  }
24334
25140
  childScope = { bindsExports: names.has(NAME), parent: scope };
24335
25141
  break;
24336
25142
  }
24337
25143
  case "CatchClause": {
24338
25144
  const names = /* @__PURE__ */ new Set();
24339
- if (node2.param) collectPattern(node2.param, names);
25145
+ if (node2.param) collectPattern2(node2.param, names);
24340
25146
  childScope = { bindsExports: names.has(NAME), parent: scope };
24341
25147
  break;
24342
25148
  }
@@ -24350,7 +25156,7 @@ function renameShadowedExports(source) {
24350
25156
  case "StaticBlock":
24351
25157
  if (node2 !== ast.body) {
24352
25158
  childScope = {
24353
- bindsExports: blockNames(node2.body).has(NAME),
25159
+ bindsExports: blockNames2(node2.body).has(NAME),
24354
25160
  parent: scope
24355
25161
  };
24356
25162
  }
@@ -24362,7 +25168,7 @@ function renameShadowedExports(source) {
24362
25168
  if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
24363
25169
  const names = /* @__PURE__ */ new Set();
24364
25170
  for (const declarator of head2.declarations) {
24365
- collectPattern(declarator.id, names);
25171
+ collectPattern2(declarator.id, names);
24366
25172
  }
24367
25173
  childScope = { bindsExports: names.has(NAME), parent: scope };
24368
25174
  }
@@ -24386,7 +25192,7 @@ function renameShadowedExports(source) {
24386
25192
  case "MemberExpression":
24387
25193
  case "MethodDefinition":
24388
25194
  case "PropertyDefinition":
24389
- skip = node2.computed ? NOTHING : PROPERTY;
25195
+ skip = node2.computed ? NOTHING2 : PROPERTY2;
24390
25196
  break;
24391
25197
  case "Property":
24392
25198
  if (node2.computed) break;
@@ -24397,19 +25203,19 @@ function renameShadowedExports(source) {
24397
25203
  }
24398
25204
  return;
24399
25205
  }
24400
- skip = PROPERTY;
25206
+ skip = PROPERTY2;
24401
25207
  break;
24402
25208
  case "LabeledStatement":
24403
25209
  case "BreakStatement":
24404
25210
  case "ContinueStatement":
24405
- skip = LABEL;
25211
+ skip = LABEL2;
24406
25212
  break;
24407
25213
  }
24408
25214
  for (const [key, value] of Object.entries(node2)) {
24409
25215
  if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
24410
25216
  if (Array.isArray(value)) {
24411
- for (const item of value) if (isNode3(item)) visit(item, childScope);
24412
- } else if (isNode3(value)) {
25217
+ for (const item of value) if (isNode4(item)) visit(item, childScope);
25218
+ } else if (isNode4(value)) {
24413
25219
  visit(value, childScope);
24414
25220
  }
24415
25221
  }
@@ -24421,73 +25227,73 @@ function resolvesToProgram(scope) {
24421
25227
  }
24422
25228
  return false;
24423
25229
  }
24424
- function hoistedNames(body) {
24425
- const names = blockNames(body);
24426
- collectVars(body, names);
25230
+ function hoistedNames2(body) {
25231
+ const names = blockNames2(body);
25232
+ collectVars2(body, names);
24427
25233
  return names;
24428
25234
  }
24429
- function blockNames(body) {
25235
+ function blockNames2(body) {
24430
25236
  const names = /* @__PURE__ */ new Set();
24431
25237
  for (const node2 of body ?? []) {
24432
25238
  if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
24433
25239
  for (const declarator of node2.declarations) {
24434
- collectPattern(declarator.id, names);
25240
+ collectPattern2(declarator.id, names);
24435
25241
  }
24436
- } else if (node2.type === "ClassDeclaration" && isNode3(node2.id)) {
25242
+ } else if (node2.type === "ClassDeclaration" && isNode4(node2.id)) {
24437
25243
  names.add(node2.id.name);
24438
- } else if (node2.type === "FunctionDeclaration" && isNode3(node2.id)) {
25244
+ } else if (node2.type === "FunctionDeclaration" && isNode4(node2.id)) {
24439
25245
  names.add(node2.id.name);
24440
25246
  }
24441
25247
  }
24442
25248
  return names;
24443
25249
  }
24444
- function collectVars(nodes, names) {
25250
+ function collectVars2(nodes, names) {
24445
25251
  if (Array.isArray(nodes)) {
24446
- for (const item of nodes) collectVars(item, names);
25252
+ for (const item of nodes) collectVars2(item, names);
24447
25253
  return;
24448
25254
  }
24449
- if (!isNode3(nodes)) return;
25255
+ if (!isNode4(nodes)) return;
24450
25256
  const node2 = nodes;
24451
25257
  if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
24452
- if (isNode3(node2.id)) names.add(node2.id.name);
25258
+ if (isNode4(node2.id)) names.add(node2.id.name);
24453
25259
  return;
24454
25260
  }
24455
25261
  if (node2.type === "VariableDeclaration" && node2.kind === "var") {
24456
25262
  for (const declarator of node2.declarations) {
24457
- collectPattern(declarator.id, names);
25263
+ collectPattern2(declarator.id, names);
24458
25264
  }
24459
25265
  }
24460
25266
  for (const [key, value] of Object.entries(node2)) {
24461
25267
  if (key === "type" || key === "start" || key === "end") continue;
24462
- collectVars(value, names);
25268
+ collectVars2(value, names);
24463
25269
  }
24464
25270
  }
24465
- function collectPattern(node2, names) {
24466
- if (!isNode3(node2)) return;
25271
+ function collectPattern2(node2, names) {
25272
+ if (!isNode4(node2)) return;
24467
25273
  switch (node2.type) {
24468
25274
  case "Identifier":
24469
25275
  names.add(node2.name);
24470
25276
  return;
24471
25277
  case "ObjectPattern":
24472
25278
  for (const property of node2.properties) {
24473
- collectPattern(property.value ?? property.argument, names);
25279
+ collectPattern2(property.value ?? property.argument, names);
24474
25280
  }
24475
25281
  return;
24476
25282
  case "ArrayPattern":
24477
- for (const element of node2.elements) collectPattern(element, names);
25283
+ for (const element of node2.elements) collectPattern2(element, names);
24478
25284
  return;
24479
25285
  case "AssignmentPattern":
24480
- collectPattern(node2.left, names);
25286
+ collectPattern2(node2.left, names);
24481
25287
  return;
24482
25288
  case "RestElement":
24483
- collectPattern(node2.argument, names);
25289
+ collectPattern2(node2.argument, names);
24484
25290
  return;
24485
25291
  default:
24486
25292
  return;
24487
25293
  }
24488
25294
  }
24489
25295
  function isExports(value) {
24490
- return isNode3(value) && value.type === "Identifier" && value.name === NAME;
25296
+ return isNode4(value) && value.type === "Identifier" && value.name === NAME;
24491
25297
  }
24492
25298
  function freshName(source) {
24493
25299
  let name = "__sandboxedjs_exports";
@@ -24495,12 +25301,12 @@ function freshName(source) {
24495
25301
  while (source.includes(name)) name = `__sandboxedjs_exports${++suffix}`;
24496
25302
  return name;
24497
25303
  }
24498
- function isNode3(value) {
25304
+ function isNode4(value) {
24499
25305
  return typeof value === "object" && value !== null && typeof value.type === "string";
24500
25306
  }
24501
- var NOTHING = [];
24502
- var PROPERTY = ["property", "key"];
24503
- var LABEL = ["label"];
25307
+ var NOTHING2 = [];
25308
+ var PROPERTY2 = ["property", "key"];
25309
+ var LABEL2 = ["label"];
24504
25310
 
24505
25311
  // src/pkg/index.ts
24506
25312
  init_path();
@@ -25246,752 +26052,356 @@ var ContainerFs = class {
25246
26052
  this.vfs.symlink(target, clean(link));
25247
26053
  }
25248
26054
  async readlink(path) {
25249
- return this.vfs.readlink(clean(path));
25250
- }
25251
- async realpath(path) {
25252
- return this.vfs.realpath(clean(path));
25253
- }
25254
- async chmod(path, mode) {
25255
- this.vfs.chmod(clean(path), mode);
25256
- }
25257
- async chown(path, uid, gid) {
25258
- this.vfs.chown(clean(path), uid, gid, ROOT_CRED);
25259
- }
25260
- /** Every path beneath `root`, depth-first. */
25261
- async walk(root = "/") {
25262
- return [...this.vfs.walk(clean(root))];
25263
- }
25264
- /** Total bytes and file counts, as `df` reports them. */
25265
- async usage(root = "/") {
25266
- return this.vfs.usage(clean(root));
25267
- }
25268
- /** Read many files at once, keyed by path. */
25269
- async readAll(paths) {
25270
- const out = {};
25271
- for (const path of paths) out[path] = this.vfs.readText(clean(path));
25272
- return out;
25273
- }
25274
- /** Write a whole map of files, creating parents. */
25275
- async writeAll(files, opts = {}) {
25276
- for (const [key, value] of Object.entries(files)) {
25277
- await this.writeFile(resolve(opts.cwd ?? "/", key), value);
25278
- }
25279
- }
25280
- };
25281
-
25282
- // src/container/session.ts
25283
- var Session = class {
25284
- constructor(kernel, init) {
25285
- this.kernel = kernel;
25286
- this.proc = kernel.procs.create({
25287
- argv: ["-sh"],
25288
- cwd: init.cwd,
25289
- env: init.env,
25290
- cred: init.cred,
25291
- ppid: 1,
25292
- kind: "shell",
25293
- tty: "pts/0"
25294
- });
25295
- this.shell = new Shell({
25296
- kernel,
25297
- proc: this.proc,
25298
- cwd: init.cwd,
25299
- env: init.env,
25300
- cred: init.cred,
25301
- scriptName: "sh",
25302
- options: { interactive: true }
25303
- });
25304
- this.hooks = init.hooks ?? {};
25305
- }
25306
- kernel;
25307
- shell;
25308
- proc;
25309
- closed = false;
25310
- hooks;
25311
- get cwd() {
25312
- return this.shell.cwd;
25313
- }
25314
- get env() {
25315
- return this.shell.vars.environment();
25316
- }
25317
- get history() {
25318
- return this.shell.history;
25319
- }
25320
- /** Run a command line, keeping every side effect for the next call. */
25321
- async run(command, opts = {}) {
25322
- if (this.closed) throw new Error("session is closed");
25323
- const combined = [];
25324
- const decoder9 = new TextDecoder();
25325
- const stdout = new BufferSink((chunk) => {
25326
- const text2 = decoder9.decode(chunk, { stream: true });
25327
- combined.push(text2);
25328
- opts.onStdout?.(text2);
25329
- this.hooks.onStdout?.(text2);
25330
- });
25331
- const stderr = new BufferSink((chunk) => {
25332
- const text2 = decoder9.decode(chunk, { stream: true });
25333
- combined.push(text2);
25334
- opts.onStderr?.(text2);
25335
- this.hooks.onStderr?.(text2);
25336
- });
25337
- if (opts.tty) {
25338
- stdout.isTTY = true;
25339
- stderr.isTTY = true;
25340
- stdout.columns = opts.columns ?? 80;
25341
- stdout.rows = opts.rows ?? 24;
25342
- }
25343
- const stdin = opts.stdin === void 0 ? new NullInput() : typeof opts.stdin === "string" || opts.stdin instanceof Uint8Array ? Pipe.from(opts.stdin) : opts.stdin;
25344
- this.shell.history.push(command);
25345
- let timer;
25346
- if (opts.timeoutMs !== void 0) {
25347
- timer = setTimeout(() => this.proc.deliver("SIGKILL"), opts.timeoutMs);
25348
- }
25349
- let exitCode;
25350
- try {
25351
- exitCode = await this.shell.execute(command, { stdin, stdout, stderr });
25352
- } finally {
25353
- if (timer) clearTimeout(timer);
25354
- }
25355
- return { exitCode, stdout: stdout.text(), stderr: stderr.text(), output: combined.join("") };
25356
- }
25357
- /** Stream a long-running command; resolves when it exits. */
25358
- async stream(command, handlers = {}) {
25359
- const result = await this.run(command, handlers);
25360
- return result.exitCode;
25361
- }
25362
- /** Feed the session an arbitrary output stream, for terminal integration. */
25363
- async pipeTo(command, stdout, stderr, stdin) {
25364
- return this.shell.execute(command, { stdin: stdin ?? new NullInput(), stdout, stderr });
25365
- }
25366
- /** True when the last command asked the shell to exit. */
25367
- get isExiting() {
25368
- return this.shell.isExiting;
25369
- }
25370
- close() {
25371
- if (this.closed) return;
25372
- this.closed = true;
25373
- this.proc.exit(0);
25374
- this.kernel.procs.remove(this.proc.pid);
25375
- }
25376
- };
25377
-
25378
- // src/node/node-child-process-bridge.ts
25379
- var KernelChildProcess = class {
25380
- pid;
25381
- command;
25382
- args;
25383
- parentPid;
25384
- state = "running";
25385
- exitCode;
25386
- stdout = "";
25387
- stderr = "";
25388
- listeners = /* @__PURE__ */ new Map();
25389
- kernel;
25390
- cred;
25391
- cwd;
25392
- env;
25393
- stdin = new Pipe();
25394
- process;
25395
- started = false;
25396
- cancelled = false;
25397
- constructor(kernel, cred, config2, pid) {
25398
- this.stdin.interactive = true;
25399
- if (config2.inheritStdio) this.stdin.isTTY = true;
25400
- if (config2.stdinIgnored) this.stdin.end();
25401
- this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
25402
- this.kernel = kernel;
25403
- this.cred = cred;
25404
- this.pid = pid;
25405
- this.command = config2.command;
25406
- this.args = config2.args?.slice() ?? [];
25407
- this.parentPid = config2.parentPid;
25408
- this.cwd = config2.cwd ?? "/";
25409
- this.env = { ...config2.env };
25410
- }
25411
- on(event, listener) {
25412
- let listeners = this.listeners.get(event);
25413
- if (!listeners) {
25414
- listeners = /* @__PURE__ */ new Set();
25415
- this.listeners.set(event, listeners);
25416
- }
25417
- listeners.add(listener);
25418
- return this;
25419
- }
25420
- emit(event, ...args) {
25421
- for (const listener of this.listeners.get(event) ?? []) listener(...args);
25422
- }
25423
- exec() {
25424
- if (this.started) return;
25425
- this.started = true;
25426
- queueMicrotask(() => {
25427
- if (this.cancelled) {
25428
- this.finish(143);
25429
- return;
25430
- }
25431
- const stdout = new ChildOutput(this, "stdout");
25432
- const stderr = new ChildOutput(this, "stderr");
25433
- try {
25434
- this.process = this.kernel.spawn([this.command, ...this.args], {
25435
- cwd: this.cwd,
25436
- env: this.env,
25437
- cred: this.cred,
25438
- stdin: this.stdin,
25439
- stdout,
25440
- stderr,
25441
- ppid: 1
25442
- });
25443
- void this.process.wait().then((code) => {
25444
- stdout.end();
25445
- stderr.end();
25446
- this.finish(code);
25447
- });
25448
- } catch (error) {
25449
- stderr.write(error instanceof Error ? error.message : String(error));
25450
- stderr.end();
25451
- stdout.end();
25452
- this.finish(1);
25453
- }
25454
- });
25455
- }
25456
- sendStdin(data) {
25457
- try {
25458
- this.stdin.write(data);
25459
- } catch {
25460
- }
25461
- }
25462
- /** Signal EOF, so a child reading stdin to the end can finish. */
25463
- endStdin() {
25464
- try {
25465
- this.stdin.end();
25466
- } catch {
25467
- }
25468
- }
25469
- kill(signal = "SIGTERM") {
25470
- if (this.process) {
25471
- this.process.deliver(signal);
25472
- return;
25473
- }
25474
- this.cancelled = true;
26055
+ return this.vfs.readlink(clean(path));
25475
26056
  }
25476
- finish(code) {
25477
- if (this.state === "exited") return;
25478
- this.state = "exited";
25479
- this.exitCode = code;
25480
- this.emit("exit", code);
26057
+ async realpath(path) {
26058
+ return this.vfs.realpath(clean(path));
25481
26059
  }
25482
- append(stream, text2) {
25483
- this[stream] += text2;
25484
- this.emit(stream, text2);
26060
+ async chmod(path, mode) {
26061
+ this.vfs.chmod(clean(path), mode);
25485
26062
  }
25486
- };
25487
- var ChildOutput = class {
25488
- constructor(child, stream) {
25489
- this.child = child;
25490
- this.stream = stream;
26063
+ async chown(path, uid, gid) {
26064
+ this.vfs.chown(clean(path), uid, gid, ROOT_CRED);
25491
26065
  }
25492
- child;
25493
- stream;
25494
- isTTY = false;
25495
- closedState = false;
25496
- decoder = new TextDecoder();
25497
- get closed() {
25498
- return this.closedState;
26066
+ /** Every path beneath `root`, depth-first. */
26067
+ async walk(root = "/") {
26068
+ return [...this.vfs.walk(clean(root))];
25499
26069
  }
25500
- write(data) {
25501
- if (this.closedState) return;
25502
- const text2 = typeof data === "string" ? data : this.decoder.decode(data, { stream: true });
25503
- if (text2) this.child.append(this.stream, text2);
26070
+ /** Total bytes and file counts, as `df` reports them. */
26071
+ async usage(root = "/") {
26072
+ return this.vfs.usage(clean(root));
25504
26073
  }
25505
- end() {
25506
- if (this.closedState) return;
25507
- this.closedState = true;
25508
- const tail2 = this.decoder.decode();
25509
- if (tail2) this.child.append(this.stream, tail2);
26074
+ /** Read many files at once, keyed by path. */
26075
+ async readAll(paths) {
26076
+ const out = {};
26077
+ for (const path of paths) out[path] = this.vfs.readText(clean(path));
26078
+ return out;
25510
26079
  }
25511
- };
25512
- var nextBridgePid = 1073741824;
25513
- function installNodeChildProcessBridge(pod, kernel, cred) {
25514
- const manager = pod.processManager;
25515
- const originalSpawn = manager.spawn;
25516
- manager.spawn = (config2) => {
25517
- const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
25518
- if (resolved?.kind === "builtin") {
25519
- return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
26080
+ /** Write a whole map of files, creating parents. */
26081
+ async writeAll(files, opts = {}) {
26082
+ for (const [key, value] of Object.entries(files)) {
26083
+ await this.writeFile(resolve(opts.cwd ?? "/", key), value);
25520
26084
  }
25521
- return originalSpawn.call(manager, config2);
25522
- };
25523
- return () => {
25524
- if (manager.spawn === originalSpawn) return;
25525
- manager.spawn = originalSpawn;
25526
- };
25527
- }
25528
-
25529
- // src/node/host-module-tracker.ts
25530
- var HostModuleTracker = class {
25531
- active = 0;
25532
- disposed = false;
25533
- dispose() {
25534
- this.disposed = true;
25535
- }
25536
- wrapped = /* @__PURE__ */ new WeakMap();
25537
- originals = /* @__PURE__ */ new WeakMap();
25538
- pending = () => this.active;
25539
- unwrap = (value) => value && (typeof value === "object" || typeof value === "function") ? this.originals.get(value) ?? value : value;
25540
- wrap(value) {
25541
- if (value === null || typeof value !== "object" && typeof value !== "function") return value;
25542
- const object = value;
25543
- if (Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;
25544
- if (this.wrapped.has(object)) return this.wrapped.get(object);
25545
- const tracker = this;
25546
- const proxy = new Proxy(object, {
25547
- // CommonJS native loaders re-export by assigning exports back onto the
25548
- // binding object. Never store a process-owned proxy in a shared module.
25549
- set(target, key, member) {
25550
- return Reflect.set(target, key, tracker.unwrap(member), target);
25551
- },
25552
- defineProperty(target, key, descriptor) {
25553
- return Reflect.defineProperty(target, key, "value" in descriptor ? { ...descriptor, value: tracker.unwrap(descriptor.value) } : descriptor);
25554
- },
25555
- get(target, key) {
25556
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
25557
- const member = Reflect.get(target, key, target);
25558
- if (descriptor && !descriptor.configurable && "value" in descriptor && !descriptor.writable) return member;
25559
- return typeof member === "function" ? tracker.wrap(member) : member;
25560
- },
25561
- apply(target, receiver, args) {
25562
- const result = Reflect.apply(target, tracker.unwrap(receiver), args.map(tracker.unwrap));
25563
- if (result && typeof result.then === "function") {
25564
- tracker.active++;
25565
- return Promise.resolve(result).then(
25566
- (value2) => {
25567
- tracker.active--;
25568
- return tracker.disposed ? new Promise(() => {
25569
- }) : tracker.wrap(value2);
25570
- },
25571
- (error) => {
25572
- tracker.active--;
25573
- if (tracker.disposed) return new Promise(() => {
25574
- });
25575
- throw error;
25576
- }
25577
- );
25578
- }
25579
- return tracker.wrap(result);
25580
- },
25581
- construct(target, args, newTarget) {
25582
- return tracker.wrap(Reflect.construct(target, args.map(tracker.unwrap), tracker.unwrap(newTarget)));
25583
- }
25584
- });
25585
- this.wrapped.set(object, proxy);
25586
- this.originals.set(proxy, object);
25587
- return proxy;
25588
26085
  }
25589
26086
  };
25590
26087
 
25591
- // src/node/commonjs-engine.ts
25592
- init_path();
25593
- var HELPERS = {
25594
- /** Import a specifier and return an ES-module-shaped namespace. */
25595
- import: "__sbxImport",
25596
- /** `import(...)`, returning a promise of a namespace. */
25597
- dynamic: "__sbxDynamicImport",
25598
- /** `export * from` — copy live bindings onto `exports`. */
25599
- exportAll: "__sbxExportAll",
25600
- /** `import.meta`. */
25601
- meta: "__sbxMeta",
25602
- /**
25603
- * The exports object, under a name of the engine's choosing.
25604
- *
25605
- * An ES module is free to declare its own top-level `exports`, `require` or
25606
- * `__dirname` — they are ordinary identifiers there, and real packages use
25607
- * all three (`const require = createRequire(import.meta.url)` is close to
25608
- * idiomatic). Naming the wrapper's binding something no source would write
25609
- * removes that entire class of collision instead of patching it up after a
25610
- * `SyntaxError`.
25611
- */
25612
- exports: "__sbxExports"
25613
- };
25614
- var DEFAULT_LOCAL = "__sbxDefault";
25615
- var MAYBE_ESM = /(^|[\s;}(])(?:import|export)(?:[\s({[*"']|$)|\bimport\s*\.\s*meta\b/;
25616
- function looksLikeEsm(source) {
25617
- return MAYBE_ESM.test(source);
25618
- }
25619
- function transformEsm(source, filename = "module.js") {
25620
- if (!looksLikeEsm(source)) return null;
25621
- let ast;
25622
- try {
25623
- ast = parseModule(source, filename);
25624
- } catch {
25625
- return null;
25626
- }
25627
- const body = ast.body;
25628
- const hasModuleSyntax = body.some(
25629
- (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
25630
- );
25631
- const usesImportMeta = !hasModuleSyntax && containsImportMeta(ast);
25632
- const esm = hasModuleSyntax || usesImportMeta;
25633
- if (!esm && !containsImportExpression(ast)) return null;
25634
- const edits = [];
25635
- const prelude = [];
25636
- const importBindings = /* @__PURE__ */ new Map();
25637
- const exportGetters = /* @__PURE__ */ new Map();
25638
- let namespaceCount = 0;
25639
- const namespaceFor = (specifier) => {
25640
- const id = `__sbxNs${namespaceCount++}`;
25641
- prelude.push(`var ${id} = ${HELPERS.import}(${JSON.stringify(specifier)});`);
25642
- return id;
25643
- };
25644
- for (const node2 of body) {
25645
- if (node2.type === "ImportDeclaration") collectImport(node2);
26088
+ // src/container/session.ts
26089
+ var Session = class {
26090
+ constructor(kernel, init) {
26091
+ this.kernel = kernel;
26092
+ this.proc = kernel.procs.create({
26093
+ argv: ["-sh"],
26094
+ cwd: init.cwd,
26095
+ env: init.env,
26096
+ cred: init.cred,
26097
+ ppid: 1,
26098
+ kind: "shell",
26099
+ tty: "pts/0"
26100
+ });
26101
+ this.shell = new Shell({
26102
+ kernel,
26103
+ proc: this.proc,
26104
+ cwd: init.cwd,
26105
+ env: init.env,
26106
+ cred: init.cred,
26107
+ scriptName: "sh",
26108
+ options: { interactive: true }
26109
+ });
26110
+ this.hooks = init.hooks ?? {};
25646
26111
  }
25647
- for (const node2 of body) {
25648
- switch (node2.type) {
25649
- case "ExportNamedDeclaration":
25650
- collectNamedExport(node2);
25651
- break;
25652
- case "ExportDefaultDeclaration":
25653
- collectDefaultExport(node2);
25654
- break;
25655
- case "ExportAllDeclaration":
25656
- collectExportAll(node2);
25657
- break;
25658
- }
26112
+ kernel;
26113
+ shell;
26114
+ proc;
26115
+ closed = false;
26116
+ hooks;
26117
+ get cwd() {
26118
+ return this.shell.cwd;
25659
26119
  }
25660
- rewriteReferences();
25661
- const header = esm ? [
25662
- `Object.defineProperty(${HELPERS.exports}, "__esModule", { value: true });`,
25663
- ...prelude,
25664
- ...[...exportGetters].map(
25665
- ([name, expression]) => `Object.defineProperty(${HELPERS.exports}, ${JSON.stringify(name)}, { enumerable: true, configurable: true, get: function () { return ${expression}; } });`
25666
- )
25667
- ].join("\n") : "";
25668
- return {
25669
- code: header ? `${header}
25670
- ${applyEdits(source, edits)}` : applyEdits(source, edits),
25671
- esm,
25672
- topLevelAwait: hasTopLevelAwait(ast)
25673
- };
25674
- function collectImport(node2) {
25675
- const specifier = node2.source.value;
25676
- const specifiers = node2.specifiers ?? [];
25677
- const id = namespaceFor(specifier);
25678
- for (const entry of specifiers) {
25679
- const local = entry.local.name ?? "";
25680
- if (entry.type === "ImportDefaultSpecifier") {
25681
- importBindings.set(local, `${id}.default`);
25682
- } else if (entry.type === "ImportNamespaceSpecifier") {
25683
- importBindings.set(local, id);
25684
- } else {
25685
- const imported = entry.imported;
25686
- const name = imported.type === "Identifier" ? imported.name : imported.value;
25687
- importBindings.set(local, `${id}[${JSON.stringify(name)}]`);
25688
- }
25689
- }
25690
- drop(node2);
26120
+ get env() {
26121
+ return this.shell.vars.environment();
25691
26122
  }
25692
- function collectNamedExport(node2) {
25693
- const declaration = node2.declaration;
25694
- if (declaration) {
25695
- for (const name of declaredNames(declaration)) exportGetters.set(name, name);
25696
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25697
- return;
26123
+ get history() {
26124
+ return this.shell.history;
26125
+ }
26126
+ /** Run a command line, keeping every side effect for the next call. */
26127
+ async run(command, opts = {}) {
26128
+ if (this.closed) throw new Error("session is closed");
26129
+ const combined = [];
26130
+ const decoder9 = new TextDecoder();
26131
+ const stdout = new BufferSink((chunk) => {
26132
+ const text2 = decoder9.decode(chunk, { stream: true });
26133
+ combined.push(text2);
26134
+ opts.onStdout?.(text2);
26135
+ this.hooks.onStdout?.(text2);
26136
+ });
26137
+ const stderr = new BufferSink((chunk) => {
26138
+ const text2 = decoder9.decode(chunk, { stream: true });
26139
+ combined.push(text2);
26140
+ opts.onStderr?.(text2);
26141
+ this.hooks.onStderr?.(text2);
26142
+ });
26143
+ const outGate = new DetachableOutput(stdout, (text2) => this.hooks.onStdout?.(text2));
26144
+ const errGate = new DetachableOutput(stderr, (text2) => this.hooks.onStderr?.(text2));
26145
+ if (opts.tty) {
26146
+ outGate.isTTY = true;
26147
+ errGate.isTTY = true;
26148
+ outGate.columns = opts.columns ?? 80;
26149
+ outGate.rows = opts.rows ?? 24;
25698
26150
  }
25699
- const source_ = node2.source;
25700
- const id = source_ ? namespaceFor(source_.value) : null;
25701
- for (const entry of node2.specifiers ?? []) {
25702
- const local = nameOf(entry.local);
25703
- const exported = nameOf(entry.exported);
25704
- const read = id ? `${id}[${JSON.stringify(local)}]` : importBindings.get(local) ?? local;
25705
- exportGetters.set(exported, read);
26151
+ const stdin = opts.stdin === void 0 ? new NullInput() : typeof opts.stdin === "string" || opts.stdin instanceof Uint8Array ? Pipe.from(opts.stdin) : opts.stdin;
26152
+ this.shell.history.push(command);
26153
+ let timer;
26154
+ if (opts.timeoutMs !== void 0) {
26155
+ timer = setTimeout(() => this.proc.deliver("SIGKILL"), opts.timeoutMs);
25706
26156
  }
25707
- drop(node2);
25708
- }
25709
- function collectDefaultExport(node2) {
25710
- const declaration = node2.declaration;
25711
- const isDeclaration = declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration";
25712
- if (isDeclaration) {
25713
- const named = declaration.id;
25714
- if (named) {
25715
- exportGetters.set("default", named.name);
25716
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25717
- return;
25718
- }
25719
- const keyword = declaration.type === "FunctionDeclaration" ? "function" : "class";
25720
- const keywordEnd = source.indexOf(keyword, declaration.start) + keyword.length;
25721
- exportGetters.set("default", DEFAULT_LOCAL);
25722
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25723
- edits.push({ start: keywordEnd, end: keywordEnd, text: ` ${DEFAULT_LOCAL}` });
25724
- return;
26157
+ let exitCode;
26158
+ try {
26159
+ exitCode = await this.shell.execute(command, { stdin, stdout: outGate, stderr: errGate });
26160
+ } finally {
26161
+ if (timer) clearTimeout(timer);
26162
+ outGate.detach();
26163
+ errGate.detach();
25725
26164
  }
25726
- exportGetters.set("default", DEFAULT_LOCAL);
25727
- edits.push({ start: node2.start, end: declaration.start, text: `var ${DEFAULT_LOCAL} = ` });
25728
- edits.push({ start: node2.end, end: node2.end, text: ";" });
26165
+ return { exitCode, stdout: stdout.text(), stderr: stderr.text(), output: combined.join("") };
25729
26166
  }
25730
- function collectExportAll(node2) {
25731
- const id = namespaceFor(node2.source.value);
25732
- const exported = node2.exported;
25733
- if (exported) exportGetters.set(nameOf(exported), id);
25734
- else prelude.push(`${HELPERS.exportAll}(${HELPERS.exports}, ${id});`);
25735
- drop(node2);
26167
+ /** Stream a long-running command; resolves when it exits. */
26168
+ async stream(command, handlers = {}) {
26169
+ const result = await this.run(command, handlers);
26170
+ return result.exitCode;
25736
26171
  }
25737
- function drop(node2) {
25738
- edits.push({ start: node2.start, end: node2.end, text: "" });
26172
+ /** Feed the session an arbitrary output stream, for terminal integration. */
26173
+ async pipeTo(command, stdout, stderr, stdin) {
26174
+ return this.shell.execute(command, { stdin: stdin ?? new NullInput(), stdout, stderr });
25739
26175
  }
25740
- function rewriteReferences() {
25741
- const programScope = { names: new Set(importBindings.keys()), parent: null };
25742
- visit(ast, programScope, true);
25743
- function visit(node2, scope, isProgram = false) {
25744
- let childScope = scope;
25745
- let skip = NOTHING2;
25746
- switch (node2.type) {
25747
- case "ImportDeclaration":
25748
- case "ExportAllDeclaration":
25749
- return;
25750
- case "ExportNamedDeclaration":
25751
- if (!node2.declaration) return;
25752
- break;
25753
- case "FunctionDeclaration":
25754
- case "FunctionExpression":
25755
- case "ArrowFunctionExpression": {
25756
- const names = /* @__PURE__ */ new Set();
25757
- for (const param of node2.params ?? []) collectPattern2(param, names);
25758
- const id = node2.id;
25759
- if (id && node2.type === "FunctionExpression") names.add(id.name);
25760
- const fnBody = node2.body;
25761
- if (fnBody?.type === "BlockStatement") {
25762
- for (const name of hoistedNames2(fnBody.body)) names.add(name);
25763
- }
25764
- childScope = { names, parent: scope };
25765
- break;
25766
- }
25767
- case "CatchClause": {
25768
- const names = /* @__PURE__ */ new Set();
25769
- if (node2.param) collectPattern2(node2.param, names);
25770
- childScope = { names, parent: scope };
25771
- break;
25772
- }
25773
- case "ClassExpression": {
25774
- const id = node2.id;
25775
- if (id) childScope = { names: /* @__PURE__ */ new Set([id.name]), parent: scope };
25776
- break;
25777
- }
25778
- case "BlockStatement":
25779
- case "StaticBlock":
25780
- if (!isProgram) {
25781
- childScope = { names: blockNames2(node2.body), parent: scope };
25782
- }
25783
- break;
25784
- case "ForStatement":
25785
- case "ForInStatement":
25786
- case "ForOfStatement": {
25787
- const head2 = node2.init ?? node2.left;
25788
- if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
25789
- const names = /* @__PURE__ */ new Set();
25790
- for (const declarator of head2.declarations) {
25791
- collectPattern2(declarator.id, names);
25792
- }
25793
- childScope = { names, parent: scope };
25794
- }
25795
- break;
25796
- }
25797
- case "MetaProperty":
25798
- edits.push({ start: node2.start, end: node2.end, text: HELPERS.meta });
25799
- return;
25800
- case "ImportExpression": {
25801
- const argument = node2.source;
25802
- edits.push({ start: node2.start, end: argument.start, text: `${HELPERS.dynamic}(` });
25803
- visit(argument, childScope);
25804
- edits.push({ start: argument.end, end: node2.end, text: ")" });
25805
- return;
25806
- }
25807
- case "Identifier": {
25808
- const replacement = lookup(node2.name, scope);
25809
- if (replacement) edits.push({ start: node2.start, end: node2.end, text: replacement });
25810
- return;
25811
- }
25812
- case "MemberExpression":
25813
- case "MethodDefinition":
25814
- case "PropertyDefinition":
25815
- skip = node2.computed ? NOTHING2 : PROPERTY2;
25816
- break;
25817
- case "Property": {
25818
- if (node2.computed) break;
25819
- if (node2.shorthand) {
25820
- const value = node2.value;
25821
- if (value.type === "Identifier") {
25822
- const replacement = lookup(value.name, scope);
25823
- if (replacement) {
25824
- edits.push({ start: value.start, end: value.end, text: `${value.name}: ${replacement}` });
25825
- return;
25826
- }
25827
- }
25828
- break;
25829
- }
25830
- skip = PROPERTY2;
25831
- break;
25832
- }
25833
- case "LabeledStatement":
25834
- case "BreakStatement":
25835
- case "ContinueStatement":
25836
- skip = LABEL2;
25837
- break;
25838
- }
25839
- for (const [key, value] of Object.entries(node2)) {
25840
- if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
25841
- if (Array.isArray(value)) {
25842
- for (const item of value) if (isNode4(item)) visit(item, childScope);
25843
- } else if (isNode4(value)) {
25844
- visit(value, childScope);
25845
- }
25846
- }
25847
- }
25848
- function lookup(name, scope) {
25849
- for (let current = scope; current; current = current.parent) {
25850
- if (current.names.has(name)) {
25851
- return current.parent === null ? importBindings.get(name) ?? null : null;
25852
- }
25853
- }
25854
- return null;
25855
- }
26176
+ /** True when the last command asked the shell to exit. */
26177
+ get isExiting() {
26178
+ return this.shell.isExiting;
26179
+ }
26180
+ close() {
26181
+ if (this.closed) return;
26182
+ this.closed = true;
26183
+ this.proc.exit(0);
26184
+ this.kernel.procs.remove(this.proc.pid);
25856
26185
  }
25857
- }
25858
- var JSX_EXTENSION = /\.[jt]sx$/;
25859
- var JsxParser = acorn.Parser.extend(jsx__default.default());
25860
- var PARSE_OPTIONS = {
25861
- ecmaVersion: "latest",
25862
- sourceType: "module",
25863
- allowAwaitOutsideFunction: true,
25864
- allowHashBang: true,
25865
- allowReturnOutsideFunction: true
25866
26186
  };
25867
- function parseModule(source, filename) {
25868
- const parser = JSX_EXTENSION.test(filename) ? JsxParser.parse.bind(JsxParser) : acorn.parse;
25869
- return parser(source, PARSE_OPTIONS);
25870
- }
25871
- function containsImportExpression(node2) {
25872
- if (Array.isArray(node2)) return node2.some(containsImportExpression);
25873
- if (!isNode4(node2)) return false;
25874
- if (node2.type === "ImportExpression") return true;
25875
- for (const [key, value] of Object.entries(node2)) {
25876
- if (key === "type" || key === "start" || key === "end") continue;
25877
- if (containsImportExpression(value)) return true;
26187
+
26188
+ // src/node/node-child-process-bridge.ts
26189
+ var KernelChildProcess = class {
26190
+ pid;
26191
+ command;
26192
+ args;
26193
+ parentPid;
26194
+ state = "running";
26195
+ exitCode;
26196
+ stdout = "";
26197
+ stderr = "";
26198
+ listeners = /* @__PURE__ */ new Map();
26199
+ kernel;
26200
+ cred;
26201
+ cwd;
26202
+ env;
26203
+ stdin = new Pipe();
26204
+ process;
26205
+ started = false;
26206
+ cancelled = false;
26207
+ constructor(kernel, cred, config2, pid) {
26208
+ this.stdin.interactive = true;
26209
+ if (config2.inheritStdio) this.stdin.isTTY = true;
26210
+ if (config2.stdinIgnored) this.stdin.end();
26211
+ this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
26212
+ this.kernel = kernel;
26213
+ this.cred = cred;
26214
+ this.pid = pid;
26215
+ this.command = config2.command;
26216
+ this.args = config2.args?.slice() ?? [];
26217
+ this.parentPid = config2.parentPid;
26218
+ this.cwd = config2.cwd ?? "/";
26219
+ this.env = { ...config2.env };
26220
+ }
26221
+ on(event, listener) {
26222
+ let listeners = this.listeners.get(event);
26223
+ if (!listeners) {
26224
+ listeners = /* @__PURE__ */ new Set();
26225
+ this.listeners.set(event, listeners);
26226
+ }
26227
+ listeners.add(listener);
26228
+ return this;
25878
26229
  }
25879
- return false;
25880
- }
25881
- function containsImportMeta(node2) {
25882
- if (Array.isArray(node2)) return node2.some(containsImportMeta);
25883
- if (!isNode4(node2)) return false;
25884
- if (node2.type === "MetaProperty") return true;
25885
- for (const [key, value] of Object.entries(node2)) {
25886
- if (key === "type" || key === "start" || key === "end") continue;
25887
- if (containsImportMeta(value)) return true;
26230
+ emit(event, ...args) {
26231
+ for (const listener of this.listeners.get(event) ?? []) listener(...args);
25888
26232
  }
25889
- return false;
25890
- }
25891
- function hasTopLevelAwait(node2) {
25892
- if (Array.isArray(node2)) return node2.some(hasTopLevelAwait);
25893
- if (!isNode4(node2)) return false;
25894
- if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
25895
- return false;
26233
+ exec() {
26234
+ if (this.started) return;
26235
+ this.started = true;
26236
+ queueMicrotask(() => {
26237
+ if (this.cancelled) {
26238
+ this.finish(143);
26239
+ return;
26240
+ }
26241
+ const stdout = new ChildOutput(this, "stdout");
26242
+ const stderr = new ChildOutput(this, "stderr");
26243
+ try {
26244
+ this.process = this.kernel.spawn([this.command, ...this.args], {
26245
+ cwd: this.cwd,
26246
+ env: this.env,
26247
+ cred: this.cred,
26248
+ stdin: this.stdin,
26249
+ stdout,
26250
+ stderr,
26251
+ ppid: 1
26252
+ });
26253
+ void this.process.wait().then((code) => {
26254
+ stdout.end();
26255
+ stderr.end();
26256
+ this.finish(code);
26257
+ });
26258
+ } catch (error) {
26259
+ stderr.write(error instanceof Error ? error.message : String(error));
26260
+ stderr.end();
26261
+ stdout.end();
26262
+ this.finish(1);
26263
+ }
26264
+ });
25896
26265
  }
25897
- if (node2.type === "AwaitExpression") return true;
25898
- if (node2.type === "ForOfStatement" && node2.await === true) return true;
25899
- for (const [key, value] of Object.entries(node2)) {
25900
- if (key === "type" || key === "start" || key === "end") continue;
25901
- if (hasTopLevelAwait(value)) return true;
26266
+ sendStdin(data) {
26267
+ try {
26268
+ this.stdin.write(data);
26269
+ } catch {
26270
+ }
25902
26271
  }
25903
- return false;
25904
- }
25905
- function declaredNames(declaration) {
25906
- const names = /* @__PURE__ */ new Set();
25907
- if (declaration.type === "VariableDeclaration") {
25908
- for (const declarator of declaration.declarations) {
25909
- collectPattern2(declarator.id, names);
26272
+ /** Signal EOF, so a child reading stdin to the end can finish. */
26273
+ endStdin() {
26274
+ try {
26275
+ this.stdin.end();
26276
+ } catch {
25910
26277
  }
25911
- } else if (isNode4(declaration.id)) {
25912
- names.add(declaration.id.name);
25913
26278
  }
25914
- return [...names];
25915
- }
25916
- function nameOf(node2) {
25917
- return node2.type === "Identifier" ? node2.name : node2.value;
25918
- }
25919
- function hoistedNames2(body) {
25920
- const names = blockNames2(body);
25921
- collectVars2(body, names);
25922
- return names;
25923
- }
25924
- function blockNames2(body) {
25925
- const names = /* @__PURE__ */ new Set();
25926
- for (const node2 of body ?? []) {
25927
- if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
25928
- for (const declarator of node2.declarations) {
25929
- collectPattern2(declarator.id, names);
25930
- }
25931
- } else if ((node2.type === "ClassDeclaration" || node2.type === "FunctionDeclaration") && isNode4(node2.id)) {
25932
- names.add(node2.id.name);
26279
+ kill(signal = "SIGTERM") {
26280
+ if (this.process) {
26281
+ this.process.deliver(signal);
26282
+ return;
25933
26283
  }
26284
+ this.cancelled = true;
25934
26285
  }
25935
- return names;
25936
- }
25937
- function collectVars2(nodes, names) {
25938
- if (Array.isArray(nodes)) {
25939
- for (const item of nodes) collectVars2(item, names);
25940
- return;
26286
+ finish(code) {
26287
+ if (this.state === "exited") return;
26288
+ this.state = "exited";
26289
+ this.exitCode = code;
26290
+ this.emit("exit", code);
25941
26291
  }
25942
- if (!isNode4(nodes)) return;
25943
- const node2 = nodes;
25944
- if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
25945
- if (isNode4(node2.id)) names.add(node2.id.name);
25946
- return;
26292
+ append(stream, text2) {
26293
+ this[stream] += text2;
26294
+ this.emit(stream, text2);
25947
26295
  }
25948
- if (node2.type === "VariableDeclaration" && node2.kind === "var") {
25949
- for (const declarator of node2.declarations) {
25950
- collectPattern2(declarator.id, names);
25951
- }
26296
+ };
26297
+ var ChildOutput = class {
26298
+ constructor(child, stream) {
26299
+ this.child = child;
26300
+ this.stream = stream;
25952
26301
  }
25953
- for (const [key, value] of Object.entries(node2)) {
25954
- if (key === "type" || key === "start" || key === "end") continue;
25955
- collectVars2(value, names);
26302
+ child;
26303
+ stream;
26304
+ isTTY = false;
26305
+ closedState = false;
26306
+ decoder = new TextDecoder();
26307
+ get closed() {
26308
+ return this.closedState;
26309
+ }
26310
+ write(data) {
26311
+ if (this.closedState) return;
26312
+ const text2 = typeof data === "string" ? data : this.decoder.decode(data, { stream: true });
26313
+ if (text2) this.child.append(this.stream, text2);
26314
+ }
26315
+ end() {
26316
+ if (this.closedState) return;
26317
+ this.closedState = true;
26318
+ const tail2 = this.decoder.decode();
26319
+ if (tail2) this.child.append(this.stream, tail2);
25956
26320
  }
26321
+ };
26322
+ var nextBridgePid = 1073741824;
26323
+ function installNodeChildProcessBridge(pod, kernel, cred) {
26324
+ const manager = pod.processManager;
26325
+ const originalSpawn = manager.spawn;
26326
+ manager.spawn = (config2) => {
26327
+ const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
26328
+ if (resolved?.kind === "builtin") {
26329
+ return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
26330
+ }
26331
+ return originalSpawn.call(manager, config2);
26332
+ };
26333
+ return () => {
26334
+ if (manager.spawn === originalSpawn) return;
26335
+ manager.spawn = originalSpawn;
26336
+ };
25957
26337
  }
25958
- function collectPattern2(node2, names) {
25959
- if (!isNode4(node2)) return;
25960
- switch (node2.type) {
25961
- case "Identifier":
25962
- names.add(node2.name);
25963
- return;
25964
- case "ObjectPattern":
25965
- for (const property of node2.properties) {
25966
- collectPattern2(property.value ?? property.argument, names);
26338
+
26339
+ // src/node/host-module-tracker.ts
26340
+ var HostModuleTracker = class {
26341
+ active = 0;
26342
+ disposed = false;
26343
+ dispose() {
26344
+ this.disposed = true;
26345
+ }
26346
+ wrapped = /* @__PURE__ */ new WeakMap();
26347
+ originals = /* @__PURE__ */ new WeakMap();
26348
+ pending = () => this.active;
26349
+ unwrap = (value) => value && (typeof value === "object" || typeof value === "function") ? this.originals.get(value) ?? value : value;
26350
+ wrap(value) {
26351
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
26352
+ const object = value;
26353
+ if (Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;
26354
+ if (this.wrapped.has(object)) return this.wrapped.get(object);
26355
+ const tracker = this;
26356
+ const proxy = new Proxy(object, {
26357
+ // CommonJS native loaders re-export by assigning exports back onto the
26358
+ // binding object. Never store a process-owned proxy in a shared module.
26359
+ set(target, key, member) {
26360
+ return Reflect.set(target, key, tracker.unwrap(member), target);
26361
+ },
26362
+ defineProperty(target, key, descriptor) {
26363
+ return Reflect.defineProperty(target, key, "value" in descriptor ? { ...descriptor, value: tracker.unwrap(descriptor.value) } : descriptor);
26364
+ },
26365
+ get(target, key) {
26366
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
26367
+ const member = Reflect.get(target, key, target);
26368
+ if (descriptor && !descriptor.configurable && "value" in descriptor && !descriptor.writable) return member;
26369
+ return typeof member === "function" ? tracker.wrap(member) : member;
26370
+ },
26371
+ apply(target, receiver, args) {
26372
+ const result = Reflect.apply(target, tracker.unwrap(receiver), args.map(tracker.unwrap));
26373
+ if (result && typeof result.then === "function") {
26374
+ tracker.active++;
26375
+ return Promise.resolve(result).then(
26376
+ (value2) => {
26377
+ tracker.active--;
26378
+ return tracker.disposed ? new Promise(() => {
26379
+ }) : tracker.wrap(value2);
26380
+ },
26381
+ (error) => {
26382
+ tracker.active--;
26383
+ if (tracker.disposed) return new Promise(() => {
26384
+ });
26385
+ throw error;
26386
+ }
26387
+ );
26388
+ }
26389
+ return tracker.wrap(result);
26390
+ },
26391
+ construct(target, args, newTarget) {
26392
+ return tracker.wrap(Reflect.construct(target, args.map(tracker.unwrap), tracker.unwrap(newTarget)));
25967
26393
  }
25968
- return;
25969
- case "ArrayPattern":
25970
- for (const element of node2.elements) collectPattern2(element, names);
25971
- return;
25972
- case "AssignmentPattern":
25973
- collectPattern2(node2.left, names);
25974
- return;
25975
- case "RestElement":
25976
- collectPattern2(node2.argument, names);
25977
- return;
25978
- default:
25979
- return;
26394
+ });
26395
+ this.wrapped.set(object, proxy);
26396
+ this.originals.set(proxy, object);
26397
+ return proxy;
25980
26398
  }
25981
- }
25982
- function applyEdits(source, edits) {
25983
- const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
25984
- let out = source;
25985
- for (const edit of ordered) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
25986
- return out;
25987
- }
25988
- function isNode4(value) {
25989
- return typeof value === "object" && value !== null && typeof value.type === "string";
25990
- }
25991
- var NOTHING2 = [];
25992
- var PROPERTY2 = ["property", "key"];
25993
- var LABEL2 = ["label"];
26399
+ };
26400
+
26401
+ // src/node/commonjs-engine.ts
26402
+ init_path();
25994
26403
  var EXTENSIONS = [".js", ".mjs", ".cjs", ".json", ".node"];
26404
+ var PREFIX_ONLY_BUILTINS = /* @__PURE__ */ new Set(["test", "test/reporters", "sea", "sqlite"]);
25995
26405
  var CONDITION_SETS = {
25996
26406
  import: [["node", "import", "module", "default"], ["node", "require", "default"], ["default"]],
25997
26407
  require: [["node", "require", "default"], ["node", "import", "module", "default"], ["default"]]
@@ -26331,7 +26741,8 @@ ${code}
26331
26741
  }
26332
26742
  builtin(specifier, importer) {
26333
26743
  const name = specifier.replace(/^node:/, "");
26334
- if (!Object.prototype.hasOwnProperty.call(this.builtins, name)) {
26744
+ const prefixOnly = PREFIX_ONLY_BUILTINS.has(name) && !specifier.startsWith("node:");
26745
+ if (prefixOnly || !Object.prototype.hasOwnProperty.call(this.builtins, name)) {
26335
26746
  return Object.prototype.hasOwnProperty.call(this.overrides, specifier) ? { found: true, value: this.overrides[specifier] } : { found: false, value: void 0 };
26336
26747
  }
26337
26748
  if (name === "module" && importer) {
@@ -27601,21 +28012,23 @@ function resolveTarget(input, overrides, defaultProtocol) {
27601
28012
  };
27602
28013
  }
27603
28014
  function isLoopback(hostname) {
27604
- return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "::" || hostname.startsWith("127.") || hostname.endsWith(".localhost");
28015
+ return isLoopbackHostname(hostname);
27605
28016
  }
27606
28017
  var VirtualClientRequest = class extends streamModule4__default.default.Writable {
27607
- constructor(target, router, fetchImpl, trackRequest) {
28018
+ constructor(target, router, fetchImpl, trackRequest, loopback = void 0) {
27608
28019
  super();
27609
28020
  this.target = target;
27610
28021
  this.router = router;
27611
28022
  this.fetchImpl = fetchImpl;
27612
28023
  this.trackRequest = trackRequest;
28024
+ this.loopback = loopback;
27613
28025
  for (const [name, value] of Object.entries(target.headers)) this.setHeader(name, value);
27614
28026
  }
27615
28027
  target;
27616
28028
  router;
27617
28029
  fetchImpl;
27618
28030
  trackRequest;
28031
+ loopback;
27619
28032
  socket = socketStub();
27620
28033
  connection = this.socket;
27621
28034
  chunks = [];
@@ -27729,7 +28142,9 @@ var VirtualClientRequest = class extends streamModule4__default.default.Writable
27729
28142
  } catch (error) {
27730
28143
  clearTimeout(this.timer);
27731
28144
  if (this.destroyedByUser) return;
27732
- const failure2 = error instanceof Error ? error : new Error(String(error));
28145
+ const thrown = error instanceof Error ? error : new Error(String(error));
28146
+ const cause = thrown.cause;
28147
+ const failure2 = cause instanceof Error && "code" in cause ? cause : thrown;
27733
28148
  if (!("code" in failure2)) Object.assign(failure2, { code: "ECONNREFUSED" });
27734
28149
  this.emit("error", failure2);
27735
28150
  } finally {
@@ -27737,18 +28152,21 @@ var VirtualClientRequest = class extends streamModule4__default.default.Writable
27737
28152
  }
27738
28153
  }
27739
28154
  async viaRouter(body) {
27740
- if (!this.router.activePortsIncludes(this.target.port)) {
28155
+ const init = {
28156
+ method: this.target.method,
28157
+ path: this.target.path,
28158
+ headers: this.getHeaders(),
28159
+ body: body.length ? new Uint8Array(body) : null
28160
+ };
28161
+ const local = this.router.activePortsIncludes(this.target.port);
28162
+ const remote = local || !this.loopback ? null : await this.loopback(this.target.port, init);
28163
+ if (!local && !remote) {
27741
28164
  throw Object.assign(
27742
28165
  new Error(`connect ECONNREFUSED ${this.target.hostname}:${this.target.port}`),
27743
28166
  { code: "ECONNREFUSED", errno: -61, syscall: "connect", address: this.target.hostname, port: this.target.port }
27744
28167
  );
27745
28168
  }
27746
- const result = await this.router.request(this.target.port, {
27747
- method: this.target.method,
27748
- path: this.target.path,
27749
- headers: this.getHeaders(),
27750
- body: body.length ? new Uint8Array(body) : null
27751
- });
28169
+ const result = remote ?? await this.router.request(this.target.port, init);
27752
28170
  const body_ = result.body;
27753
28171
  const bytes2 = typeof body_ === "string" ? new TextEncoder().encode(body_) : body_ instanceof ArrayBuffer ? new Uint8Array(body_) : body_ ?? new Uint8Array();
27754
28172
  const status = result.statusCode ?? 200;
@@ -27788,7 +28206,13 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
27788
28206
  const request = (input, second, third) => {
27789
28207
  const overrides = second && typeof second === "object" && !(second instanceof Function) ? second : void 0;
27790
28208
  const callback = [second, third].find((value) => typeof value === "function");
27791
- const req = new VirtualClientRequest(resolveTarget(input, overrides, defaultProtocol), router, options.fetch, options.trackRequest);
28209
+ const req = new VirtualClientRequest(
28210
+ resolveTarget(input, overrides, defaultProtocol),
28211
+ router,
28212
+ options.fetch,
28213
+ options.trackRequest,
28214
+ options.loopback
28215
+ );
27792
28216
  if (callback) req.on("response", callback);
27793
28217
  return req;
27794
28218
  };
@@ -27815,6 +28239,62 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
27815
28239
  }
27816
28240
  };
27817
28241
  }
28242
+ function createVirtualFetch(router, options = {}) {
28243
+ return (async (input, init) => {
28244
+ const request = new Request(input, init);
28245
+ const url = new URL(request.url);
28246
+ const release = options.trackRequest?.();
28247
+ try {
28248
+ if (!isLoopback(url.hostname)) {
28249
+ if (!options.fetch) {
28250
+ throw Object.assign(new TypeError("fetch failed"), {
28251
+ cause: Object.assign(new Error(`getaddrinfo ENOTFOUND ${url.hostname}`), { code: "ENOTFOUND" })
28252
+ });
28253
+ }
28254
+ return await options.fetch(request);
28255
+ }
28256
+ const port = Number(url.port || (url.protocol === "https:" ? 443 : 80));
28257
+ const headers = {};
28258
+ request.headers.forEach((value, key) => {
28259
+ headers[key] = value;
28260
+ });
28261
+ const bodyless = request.method === "GET" || request.method === "HEAD";
28262
+ const loopbackRequest = {
28263
+ method: request.method,
28264
+ path: `${url.pathname}${url.search}`,
28265
+ headers,
28266
+ body: bodyless ? null : new Uint8Array(await request.arrayBuffer())
28267
+ };
28268
+ const result = router.activePortsIncludes(port) ? await router.request(port, loopbackRequest) : await options.loopback?.(port, loopbackRequest) ?? null;
28269
+ if (!result) {
28270
+ throw Object.assign(new TypeError("fetch failed"), {
28271
+ cause: Object.assign(new Error(`connect ECONNREFUSED ${url.hostname}:${port}`), {
28272
+ code: "ECONNREFUSED",
28273
+ errno: -61,
28274
+ syscall: "connect",
28275
+ address: url.hostname,
28276
+ port
28277
+ })
28278
+ });
28279
+ }
28280
+ const status = result.statusCode ?? 200;
28281
+ const responseHeaders = new Headers();
28282
+ for (const [name, value] of Object.entries(result.headers ?? {})) {
28283
+ for (const part of Array.isArray(value) ? value : [value]) responseHeaders.append(name, String(part));
28284
+ }
28285
+ const raw = result.body;
28286
+ const bytes2 = typeof raw === "string" ? new TextEncoder().encode(raw) : raw instanceof ArrayBuffer ? new Uint8Array(raw) : raw ?? new Uint8Array();
28287
+ const nullBody = status === 204 || status === 205 || status === 304 || request.method === "HEAD";
28288
+ return new Response(nullBody ? null : new Uint8Array(bytes2), {
28289
+ status,
28290
+ statusText: result.statusMessage ?? STATUS_CODES[status] ?? "",
28291
+ headers: responseHeaders
28292
+ });
28293
+ } finally {
28294
+ release?.();
28295
+ }
28296
+ });
28297
+ }
27818
28298
  function validateHeaderName(name) {
27819
28299
  if (!/^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/.test(name)) throw Object.assign(new TypeError(`Header name must be a valid HTTP token [${name}]`), { code: "ERR_INVALID_HTTP_TOKEN" });
27820
28300
  }
@@ -27834,6 +28314,492 @@ function socketStub() {
27834
28314
  }
27835
28315
  var METHODS = ["ACL", "BIND", "CHECKOUT", "CONNECT", "COPY", "DELETE", "GET", "HEAD", "LINK", "LOCK", "M-SEARCH", "MERGE", "MKACTIVITY", "MKCALENDAR", "MKCOL", "MOVE", "NOTIFY", "OPTIONS", "PATCH", "POST", "PROPFIND", "PROPPATCH", "PURGE", "PUT", "REBIND", "REPORT", "SEARCH", "SOURCE", "SUBSCRIBE", "TRACE", "UNBIND", "UNLINK", "UNLOCK", "UNSUBSCRIBE"];
27836
28316
  var STATUS_CODES = { 100: "Continue", 200: "OK", 201: "Created", 202: "Accepted", 204: "No Content", 206: "Partial Content", 301: "Moved Permanently", 302: "Found", 304: "Not Modified", 307: "Temporary Redirect", 308: "Permanent Redirect", 400: "Bad Request", 401: "Unauthorized", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 409: "Conflict", 413: "Payload Too Large", 415: "Unsupported Media Type", 422: "Unprocessable Entity", 429: "Too Many Requests", 500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable" };
28317
+
28318
+ // src/node/test-module.ts
28319
+ function parseArguments(args) {
28320
+ let name = "<anonymous>";
28321
+ let options = {};
28322
+ let fn;
28323
+ for (const arg of args) {
28324
+ if (typeof arg === "string") name = arg;
28325
+ else if (typeof arg === "function") {
28326
+ fn = arg;
28327
+ if (name === "<anonymous>" && arg.name) name = arg.name;
28328
+ } else if (arg && typeof arg === "object") options = arg;
28329
+ }
28330
+ return { name, options, fn };
28331
+ }
28332
+ function errorText(error) {
28333
+ if (error instanceof Error) {
28334
+ const stack = error.stack ?? `${error.name}: ${error.message}`;
28335
+ return stack.includes(error.message) ? stack : `${error.name}: ${error.message}
28336
+ ${stack}`;
28337
+ }
28338
+ return String(error);
28339
+ }
28340
+ function indent(text2, spaces) {
28341
+ const pad2 = " ".repeat(spaces);
28342
+ return text2.split("\n").map((line) => line ? pad2 + line : line).join("\n");
28343
+ }
28344
+ function duration(started) {
28345
+ return (performance.now() - started).toFixed(6).replace(/\.?0+$/, "");
28346
+ }
28347
+ function invoke(host2, fn, context, timeout) {
28348
+ const body = new Promise((resolve3, reject) => {
28349
+ if (fn.length >= 2) {
28350
+ let settled = false;
28351
+ const done = (error) => {
28352
+ if (settled) return;
28353
+ settled = true;
28354
+ if (error) reject(error);
28355
+ else resolve3();
28356
+ };
28357
+ try {
28358
+ const returned = fn(context, done);
28359
+ if (returned && typeof returned.then === "function") {
28360
+ reject(new Error("passed a callback but also returned a Promise"));
28361
+ }
28362
+ } catch (error) {
28363
+ done(error);
28364
+ }
28365
+ return;
28366
+ }
28367
+ try {
28368
+ Promise.resolve(fn(context)).then(() => resolve3(), reject);
28369
+ } catch (error) {
28370
+ reject(error);
28371
+ }
28372
+ });
28373
+ if (timeout === void 0 || !Number.isFinite(timeout)) return body;
28374
+ return new Promise((resolve3, reject) => {
28375
+ const timer = host2.setTimeout(() => reject(new Error(`test timed out after ${timeout}ms`)), timeout);
28376
+ body.then(
28377
+ () => {
28378
+ host2.clearTimeout(timer);
28379
+ resolve3();
28380
+ },
28381
+ (error) => {
28382
+ host2.clearTimeout(timer);
28383
+ reject(error);
28384
+ }
28385
+ );
28386
+ });
28387
+ }
28388
+ function createMockTracker() {
28389
+ const restorers = [];
28390
+ const fn = (original = () => void 0, implementation = original) => {
28391
+ let current = implementation;
28392
+ const once = /* @__PURE__ */ new Map();
28393
+ const calls = [];
28394
+ const mocked = function(...args) {
28395
+ const index = calls.length;
28396
+ const chosen = once.get(index) ?? current;
28397
+ once.delete(index);
28398
+ const call = { arguments: args, result: void 0, error: void 0, this: this };
28399
+ calls.push(call);
28400
+ try {
28401
+ call.result = chosen.apply(this, args);
28402
+ return call.result;
28403
+ } catch (error) {
28404
+ call.error = error;
28405
+ throw error;
28406
+ }
28407
+ };
28408
+ Object.defineProperty(mocked, "mock", {
28409
+ value: {
28410
+ calls,
28411
+ callCount: () => calls.length,
28412
+ resetCalls: () => {
28413
+ calls.length = 0;
28414
+ },
28415
+ mockImplementation: (next) => {
28416
+ current = next;
28417
+ },
28418
+ mockImplementationOnce: (next, onCall) => {
28419
+ once.set(onCall ?? calls.length, next);
28420
+ },
28421
+ restore: () => {
28422
+ current = original;
28423
+ }
28424
+ }
28425
+ });
28426
+ return mocked;
28427
+ };
28428
+ const method = (object, name, implementation) => {
28429
+ const original = object[name];
28430
+ if (typeof original !== "function") {
28431
+ throw new TypeError(`The property '${name}' is not a function and cannot be mocked`);
28432
+ }
28433
+ const mocked = fn(original, implementation ?? original);
28434
+ object[name] = mocked;
28435
+ const restore = () => {
28436
+ object[name] = original;
28437
+ };
28438
+ mocked.mock.restore = restore;
28439
+ restorers.push(restore);
28440
+ return mocked;
28441
+ };
28442
+ return {
28443
+ fn,
28444
+ method,
28445
+ reset: () => {
28446
+ for (const restore of restorers.splice(0)) restore();
28447
+ },
28448
+ restoreAll: () => {
28449
+ for (const restore of restorers.splice(0)) restore();
28450
+ }
28451
+ };
28452
+ }
28453
+ function createTestModule(host2) {
28454
+ const root = {
28455
+ kind: "suite",
28456
+ name: "",
28457
+ options: {},
28458
+ parent: null,
28459
+ children: [],
28460
+ hooks: { before: [], after: [], beforeEach: [], afterEach: [] }
28461
+ };
28462
+ let collecting = root;
28463
+ let scheduled = false;
28464
+ let running = null;
28465
+ const mock = createMockTracker();
28466
+ const counts = { tests: 0, suites: 0, pass: 0, fail: 0, cancelled: 0, skipped: 0, todo: 0 };
28467
+ const failures = [];
28468
+ let tapIndex = 0;
28469
+ let onlyWarned = false;
28470
+ const settings = () => host2.settings();
28471
+ const tap = () => settings().reporter === "tap";
28472
+ const schedule = () => {
28473
+ if (scheduled) return;
28474
+ scheduled = true;
28475
+ host2.setTimeout(() => {
28476
+ running = runAll();
28477
+ }, 0);
28478
+ };
28479
+ const fullName = (node2) => {
28480
+ const parts = [];
28481
+ let cursor = node2;
28482
+ while (cursor && cursor !== root) {
28483
+ parts.unshift(cursor.name);
28484
+ cursor = cursor.parent;
28485
+ }
28486
+ return parts.join(" ");
28487
+ };
28488
+ const matchesPattern2 = (name) => {
28489
+ const patterns = settings().namePatterns ?? [];
28490
+ if (patterns.length === 0) return true;
28491
+ return patterns.some((pattern) => {
28492
+ try {
28493
+ return new RegExp(pattern).test(name);
28494
+ } catch {
28495
+ return name.includes(pattern);
28496
+ }
28497
+ });
28498
+ };
28499
+ const hasOnly = (node2) => Boolean(node2.options.only) || node2.kind === "suite" && node2.children.some(hasOnly);
28500
+ const report = (depth, outcome, name, started, detail) => {
28501
+ if (tap()) {
28502
+ tapIndex += 1;
28503
+ const ok2 = outcome === "fail" ? "not ok" : "ok";
28504
+ const directive = outcome === "skip" ? " # SKIP" : outcome === "todo" ? " # TODO" : outcome === "cancelled" ? " # CANCELLED" : "";
28505
+ host2.write(`${ok2} ${tapIndex} - ${name}${directive}
28506
+ `);
28507
+ host2.write(` ---
28508
+ duration_ms: ${duration(started)}
28509
+ `);
28510
+ if (detail) host2.write(` error: |-
28511
+ ${indent(detail, 4)}
28512
+ `);
28513
+ host2.write(" ...\n");
28514
+ return;
28515
+ }
28516
+ const pad2 = " ".repeat(depth);
28517
+ const mark = outcome === "pass" ? "\u2714" : outcome === "fail" ? "\u2716" : outcome === "cancelled" ? "\u2716" : "\uFE63";
28518
+ const suffix = outcome === "skip" ? " # SKIP" : outcome === "todo" ? " # TODO" : "";
28519
+ host2.write(`${pad2}${mark} ${name} (${duration(started)}ms)${suffix}
28520
+ `);
28521
+ if (detail) host2.write(`${indent(detail, depth * 2 + 2)}
28522
+ `);
28523
+ };
28524
+ const record = (outcome, name, error) => {
28525
+ counts.tests += 1;
28526
+ if (outcome === "pass") counts.pass += 1;
28527
+ else if (outcome === "fail") {
28528
+ counts.fail += 1;
28529
+ failures.push({ name, error });
28530
+ } else if (outcome === "cancelled") {
28531
+ counts.cancelled += 1;
28532
+ failures.push({ name, error });
28533
+ } else if (outcome === "skip") counts.skipped += 1;
28534
+ else counts.todo += 1;
28535
+ };
28536
+ const ancestors = (suite) => {
28537
+ const chain = [];
28538
+ for (let cursor = suite; cursor; cursor = cursor.parent) chain.unshift(cursor);
28539
+ return chain;
28540
+ };
28541
+ const runTest = async (name, display, options, fn, depth, suite, extraHooks, runOnly) => {
28542
+ const started = performance.now();
28543
+ if (!matchesPattern2(display)) return true;
28544
+ if (runOnly && !options.only) return true;
28545
+ if (options.skip) {
28546
+ record("skip", display);
28547
+ report(depth, "skip", name, started);
28548
+ return true;
28549
+ }
28550
+ if (!fn) {
28551
+ const outcome = options.todo ? "todo" : "skip";
28552
+ record(outcome, display);
28553
+ report(depth, outcome, name, started);
28554
+ return true;
28555
+ }
28556
+ const controller = new AbortController();
28557
+ const subtests = [];
28558
+ const ownHooks = { before: [], after: [], beforeEach: [], afterEach: [] };
28559
+ let skipped = false;
28560
+ let todo = options.todo ?? false;
28561
+ const context = {
28562
+ name,
28563
+ fullName: display,
28564
+ signal: controller.signal,
28565
+ mock,
28566
+ test: (...args) => {
28567
+ const sub = parseArguments(args);
28568
+ const promise = runTest(
28569
+ sub.name,
28570
+ `${display} ${sub.name}`,
28571
+ sub.options,
28572
+ sub.fn,
28573
+ depth + 1,
28574
+ suite,
28575
+ ownHooks,
28576
+ false
28577
+ );
28578
+ subtests.push(promise);
28579
+ return promise.then(() => void 0);
28580
+ },
28581
+ skip: (message) => {
28582
+ skipped = message ?? true;
28583
+ },
28584
+ todo: (message) => {
28585
+ todo = message ?? true;
28586
+ },
28587
+ diagnostic: (message) => {
28588
+ host2.write(tap() ? `# ${message}
28589
+ ` : `${" ".repeat(depth + 1)}\u2139 ${message}
28590
+ `);
28591
+ },
28592
+ plan: () => {
28593
+ },
28594
+ before: (hook2) => ownHooks.before.push(hook2),
28595
+ after: (hook2) => ownHooks.after.push(hook2),
28596
+ beforeEach: (hook2) => ownHooks.beforeEach.push(hook2),
28597
+ afterEach: (hook2) => ownHooks.afterEach.push(hook2)
28598
+ };
28599
+ let error;
28600
+ try {
28601
+ const chain = ancestors(suite);
28602
+ for (const level of chain) for (const hook2 of level.hooks.beforeEach) await invoke(host2, hook2, context, void 0);
28603
+ for (const hook2 of extraHooks.beforeEach) await invoke(host2, hook2, context, void 0);
28604
+ await invoke(host2, fn, context, options.timeout);
28605
+ const results = await Promise.all(subtests);
28606
+ for (const hook2 of ownHooks.after) await invoke(host2, hook2, context, void 0);
28607
+ if (results.includes(false)) error = new Error(`${results.filter((ok2) => !ok2).length} subtest(s) failed`);
28608
+ for (const hook2 of extraHooks.afterEach) await invoke(host2, hook2, context, void 0);
28609
+ for (const level of [...chain].reverse()) for (const hook2 of level.hooks.afterEach) await invoke(host2, hook2, context, void 0);
28610
+ } catch (caught) {
28611
+ error = caught;
28612
+ } finally {
28613
+ controller.abort();
28614
+ }
28615
+ if (skipped) {
28616
+ record("skip", display);
28617
+ report(depth, "skip", name, started);
28618
+ return true;
28619
+ }
28620
+ if (error !== void 0) {
28621
+ if (todo) {
28622
+ record("todo", display);
28623
+ report(depth, "todo", name, started);
28624
+ return true;
28625
+ }
28626
+ record("fail", display, error);
28627
+ report(depth, "fail", name, started, errorText(error));
28628
+ return false;
28629
+ }
28630
+ record(todo ? "todo" : "pass", display);
28631
+ report(depth, todo ? "todo" : "pass", name, started);
28632
+ return true;
28633
+ };
28634
+ const runSuite = async (suite, depth, runOnly) => {
28635
+ const started = performance.now();
28636
+ const display = fullName(suite);
28637
+ const isRoot = suite === root;
28638
+ if (!isRoot) {
28639
+ counts.suites += 1;
28640
+ if (tap()) host2.write(`# Subtest: ${suite.name}
28641
+ `);
28642
+ else host2.write(`${" ".repeat(depth)}\u25B6 ${suite.name}
28643
+ `);
28644
+ if (suite.options.skip) {
28645
+ for (const child of suite.children) if (child.kind === "test") record("skip", fullName(child));
28646
+ report(depth, "skip", suite.name, started);
28647
+ return true;
28648
+ }
28649
+ }
28650
+ const context = { name: suite.name, signal: new AbortController().signal };
28651
+ let ok2 = true;
28652
+ try {
28653
+ for (const hook2 of suite.hooks.before) await invoke(host2, hook2, context, void 0);
28654
+ } catch (error) {
28655
+ for (const child of suite.children) {
28656
+ if (child.kind === "test") {
28657
+ record("cancelled", fullName(child), error);
28658
+ report(depth + (isRoot ? 0 : 1), "cancelled", child.name, started, errorText(error));
28659
+ }
28660
+ }
28661
+ if (!isRoot) report(depth, "fail", suite.name, started);
28662
+ return false;
28663
+ }
28664
+ const only = runOnly || settings().only === true && suite.children.some(hasOnly);
28665
+ for (const child of suite.children) {
28666
+ const childDepth = depth + (isRoot ? 0 : 1);
28667
+ if (child.kind === "suite") {
28668
+ if (only && !hasOnly(child) && !child.options.only) continue;
28669
+ ok2 = await runSuite(child, childDepth, only && !child.options.only) && ok2;
28670
+ } else {
28671
+ ok2 = await runTest(
28672
+ child.name,
28673
+ fullName(child),
28674
+ child.options,
28675
+ child.fn,
28676
+ childDepth,
28677
+ suite,
28678
+ { before: [], after: [], beforeEach: [], afterEach: [] },
28679
+ only
28680
+ ) && ok2;
28681
+ }
28682
+ }
28683
+ try {
28684
+ for (const hook2 of suite.hooks.after) await invoke(host2, hook2, context, void 0);
28685
+ } catch (error) {
28686
+ ok2 = false;
28687
+ failures.push({ name: `${display || "<root>"} (after hook)`, error });
28688
+ host2.write(`${indent(`\u2716 after hook failed
28689
+ ${errorText(error)}`, depth * 2)}
28690
+ `);
28691
+ }
28692
+ if (!isRoot) {
28693
+ if (tap()) host2.write(`${ok2 ? "ok" : "not ok"} ${++tapIndex} - ${suite.name}
28694
+ `);
28695
+ else host2.write(`${" ".repeat(depth)}${ok2 ? "\u25B6" : "\u2716"} ${suite.name} (${duration(started)}ms)
28696
+ `);
28697
+ }
28698
+ return ok2;
28699
+ };
28700
+ const runAll = async () => {
28701
+ const release = host2.keepAlive();
28702
+ const started = performance.now();
28703
+ if (tap()) host2.write("TAP version 13\n");
28704
+ if (!settings().only && !onlyWarned && root.children.some(hasOnly)) {
28705
+ onlyWarned = true;
28706
+ host2.write("\u2139 'only' and 'runOnly' require the --test-only command-line option.\n");
28707
+ }
28708
+ let ok2 = true;
28709
+ try {
28710
+ ok2 = await runSuite(root, 0, false);
28711
+ } catch (error) {
28712
+ ok2 = false;
28713
+ failures.push({ name: "<test runner>", error });
28714
+ }
28715
+ mock.restoreAll();
28716
+ if (tap()) {
28717
+ host2.write(`1..${tapIndex}
28718
+ `);
28719
+ for (const [key, value] of Object.entries(counts)) host2.write(`# ${key} ${value}
28720
+ `);
28721
+ host2.write(`# duration_ms ${duration(started)}
28722
+ `);
28723
+ } else {
28724
+ for (const [key, value] of Object.entries(counts)) host2.write(`\u2139 ${key} ${value}
28725
+ `);
28726
+ host2.write(`\u2139 duration_ms ${duration(started)}
28727
+ `);
28728
+ if (failures.length > 0) {
28729
+ host2.write("\n\u2716 failing tests:\n\n");
28730
+ for (const failure2 of failures) host2.write(`test at ${failure2.name}
28731
+ ${indent(errorText(failure2.error), 2)}
28732
+
28733
+ `);
28734
+ }
28735
+ }
28736
+ release();
28737
+ host2.exit(ok2 && counts.fail === 0 && counts.cancelled === 0 ? 0 : 1);
28738
+ };
28739
+ const declareTest = (extra) => (...args) => {
28740
+ const { name, options, fn } = parseArguments(args);
28741
+ if (running && collecting === root && scheduled) {
28742
+ host2.write(`\u2716 test "${name}" was declared after the run started and did not run
28743
+ `);
28744
+ return Promise.resolve();
28745
+ }
28746
+ collecting.children.push({ kind: "test", name, options: { ...options, ...extra }, parent: collecting, fn });
28747
+ schedule();
28748
+ return Promise.resolve();
28749
+ };
28750
+ const declareSuite = (extra) => (...args) => {
28751
+ const { name, options, fn } = parseArguments(args);
28752
+ const suite = {
28753
+ kind: "suite",
28754
+ name,
28755
+ options: { ...options, ...extra },
28756
+ parent: collecting,
28757
+ children: [],
28758
+ hooks: { before: [], after: [], beforeEach: [], afterEach: [] }
28759
+ };
28760
+ collecting.children.push(suite);
28761
+ schedule();
28762
+ const previous = collecting;
28763
+ collecting = suite;
28764
+ try {
28765
+ fn?.({ name, signal: new AbortController().signal });
28766
+ } finally {
28767
+ collecting = previous;
28768
+ }
28769
+ return Promise.resolve();
28770
+ };
28771
+ const withModifiers = (make) => {
28772
+ const base2 = make({});
28773
+ base2.skip = make({ skip: true });
28774
+ base2.todo = make({ todo: true });
28775
+ base2.only = make({ only: true });
28776
+ return base2;
28777
+ };
28778
+ const test = withModifiers(declareTest);
28779
+ const describe2 = withModifiers(declareSuite);
28780
+ const hook = (kind) => (fn) => {
28781
+ collecting.hooks[kind].push(fn);
28782
+ schedule();
28783
+ };
28784
+ Object.assign(test, {
28785
+ test,
28786
+ it: test,
28787
+ describe: describe2,
28788
+ suite: describe2,
28789
+ before: hook("before"),
28790
+ after: hook("after"),
28791
+ beforeEach: hook("beforeEach"),
28792
+ afterEach: hook("afterEach"),
28793
+ mock,
28794
+ run: () => {
28795
+ throw Object.assign(new Error("node:test run() is not available in this container; use `node --test`"), {
28796
+ code: "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM"
28797
+ });
28798
+ }
28799
+ });
28800
+ Object.defineProperty(test, "default", { value: test, enumerable: false });
28801
+ return test;
28802
+ }
27837
28803
  var hashes = { md5: legacy$1.md5, sha1: legacy$1.sha1, "sha-1": legacy$1.sha1, sha224: sha256.sha224, "sha-224": sha256.sha224, sha256: sha256.sha256, "sha-256": sha256.sha256, sha384: sha512.sha384, "sha-384": sha512.sha384, sha512: sha512.sha512, "sha-512": sha512.sha512 };
27838
28804
  function createCryptoModule() {
27839
28805
  const cryptoObject = globalThis.crypto;
@@ -28790,7 +29756,7 @@ function createCoreModules(options) {
28790
29756
  });
28791
29757
  const stderrWrite = options.stderr ?? (() => {
28792
29758
  });
28793
- const timers = createTrackedTimers();
29759
+ const timers = createTrackedTimers((error) => reportUncaught(error));
28794
29760
  const processObject = Object.assign(new EventEmitter4__default.default(), processShim__default.default, {
28795
29761
  argv: options.argv?.slice() ?? ["/usr/bin/node"],
28796
29762
  argv0: "node",
@@ -28820,16 +29786,65 @@ function createCoreModules(options) {
28820
29786
  uptime: () => performance.now() / 1e3,
28821
29787
  memoryUsage: () => ({ rss: 0, heapTotal: 0, heapUsed: 0, external: 0, arrayBuffers: 0 }),
28822
29788
  cpuUsage: () => ({ user: 0, system: 0 }),
28823
- exit: (code = 0) => {
28824
- exitCode = code;
28825
- processObject.exitCode = code;
28826
- options.onExit?.(code);
29789
+ /* A bare `process.exit()` ends with `process.exitCode`, as in Node. */
29790
+ exit: (code) => {
29791
+ const status = normalizeExitCode(code ?? processObject.exitCode);
29792
+ exitCode = status;
29793
+ processObject.exitCode = status;
29794
+ emitExit(status);
29795
+ options.onExit?.(status);
29796
+ },
29797
+ nextTick: (fn, ...args) => {
29798
+ queueMicrotask(() => {
29799
+ try {
29800
+ fn(...args);
29801
+ } catch (error) {
29802
+ reportUncaught(error);
29803
+ }
29804
+ });
28827
29805
  },
28828
29806
  kill: () => true
28829
29807
  });
29808
+ for (const method of EVENT_EMITTER_METHODS) {
29809
+ const implementation = EventEmitter4__default.default.prototype[method];
29810
+ if (typeof implementation === "function") processObject[method] = implementation;
29811
+ }
28830
29812
  const tty = options.tty === true;
28831
29813
  processObject.stdout = makeOutputStream(stdoutWrite, 1, tty);
28832
29814
  processObject.stderr = makeOutputStream(stderrWrite, 2, tty);
29815
+ let exiting = false;
29816
+ const emitExit = (status) => {
29817
+ if (exiting) return;
29818
+ exiting = true;
29819
+ try {
29820
+ processObject.emit("exit", status);
29821
+ } catch {
29822
+ }
29823
+ };
29824
+ const reportUncaught = (error, origin = "uncaughtException") => {
29825
+ if (!exiting && processObject.listenerCount(origin) > 0) {
29826
+ try {
29827
+ processObject.emit(origin, error, origin === "uncaughtException" ? origin : void 0);
29828
+ return;
29829
+ } catch (handlerError) {
29830
+ error = handlerError;
29831
+ }
29832
+ }
29833
+ stderrWrite(formatUncaught(error));
29834
+ try {
29835
+ processObject.exit(1);
29836
+ } catch {
29837
+ }
29838
+ };
29839
+ const defer = (fn) => {
29840
+ queueMicrotask(() => {
29841
+ try {
29842
+ fn();
29843
+ } catch (error) {
29844
+ reportUncaught(error);
29845
+ }
29846
+ });
29847
+ };
28833
29848
  const stdin = options.interactiveStdin ? new streamModule4__default.default.PassThrough() : new streamModule4__default.default.Readable({ read() {
28834
29849
  this.push(null);
28835
29850
  } });
@@ -28849,7 +29864,7 @@ function createCoreModules(options) {
28849
29864
  });
28850
29865
  processObject.stdin = stdin;
28851
29866
  Reflect.deleteProperty(processObject, "browser");
28852
- const fs = createFsModule(volume, () => cwd, options.stdinPath);
29867
+ const fs = createFsModule(volume, () => cwd, options.stdinPath, defer);
28853
29868
  const path = createPathModule(() => cwd);
28854
29869
  const consoleObject = new Console(stdoutWrite, stderrWrite);
28855
29870
  const os = createOsModule();
@@ -28862,20 +29877,24 @@ function createCoreModules(options) {
28862
29877
  }
28863
29878
  };
28864
29879
  let inFlightRequests = 0;
29880
+ const trackRequest = () => {
29881
+ inFlightRequests++;
29882
+ return () => {
29883
+ inFlightRequests--;
29884
+ };
29885
+ };
29886
+ const ambientFetch = options.http?.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
29887
+ const outboundFetch = options.http?.policy && ambientFetch ? policedFetch(options.http.policy, ambientFetch) : ambientFetch;
28865
29888
  const httpOptions = {
28866
- ...options.http?.fetch ? { fetch: options.http.fetch } : {},
28867
- trackRequest: () => {
28868
- inFlightRequests++;
28869
- return () => {
28870
- inFlightRequests--;
28871
- };
28872
- }
29889
+ ...outboundFetch ? { fetch: outboundFetch } : {},
29890
+ ...options.http?.loopback ? { loopback: options.http.loopback } : {},
29891
+ trackRequest
28873
29892
  };
28874
29893
  const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
28875
29894
  const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
28876
29895
  const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
28877
29896
  const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
28878
- const dns = createDnsModule();
29897
+ const dns = createDnsModule(defer);
28879
29898
  const builtins = {
28880
29899
  assert: assert_module_default,
28881
29900
  "assert/strict": assert_module_default.strict ?? assert_module_default,
@@ -28911,7 +29930,27 @@ function createCoreModules(options) {
28911
29930
  url: url_module_default,
28912
29931
  util: util_module_default,
28913
29932
  "util/types": util_module_default.types ?? {},
28914
- zlib: zlib_module_default
29933
+ zlib: zlib_module_default,
29934
+ test: createTestModule({
29935
+ write: stdoutWrite,
29936
+ setTimeout: (fn, ms) => timers.api.setTimeout(fn, ms),
29937
+ clearTimeout: (handle) => timers.api.clearTimeout(handle),
29938
+ /* A pending interval is what keeps a process alive in this runtime; a test
29939
+ * awaiting nothing but promises would otherwise be judged finished. */
29940
+ keepAlive: () => {
29941
+ const handle = timers.api.setInterval(() => {
29942
+ }, 1 << 30);
29943
+ return () => timers.api.clearInterval(handle);
29944
+ },
29945
+ exit: (code) => {
29946
+ processObject.exitCode = code;
29947
+ try {
29948
+ processObject.exit(code);
29949
+ } catch {
29950
+ }
29951
+ },
29952
+ settings: () => parseTestSettings(env2.SANDBOXEDJS_NODE_TEST)
29953
+ })
28915
29954
  };
28916
29955
  for (const name of stubNames) builtins[name] = createUnsupportedModule(name);
28917
29956
  builtins.net = createNetModule();
@@ -28919,8 +29958,26 @@ function createCoreModules(options) {
28919
29958
  Buffer: Buffer2,
28920
29959
  console: consoleObject,
28921
29960
  process: processObject,
28922
- ...timers.api
29961
+ ...timers.api,
29962
+ queueMicrotask: (fn) => queueMicrotask(() => {
29963
+ try {
29964
+ fn();
29965
+ } catch (error) {
29966
+ reportUncaught(error);
29967
+ }
29968
+ })
28923
29969
  };
29970
+ if (options.http) {
29971
+ globals.fetch = createVirtualFetch(options.http.router, {
29972
+ ...outboundFetch ? { fetch: outboundFetch } : {},
29973
+ ...options.http.loopback ? { loopback: options.http.loopback } : {},
29974
+ trackRequest
29975
+ });
29976
+ const NativeWebSocket = globalThis.WebSocket;
29977
+ if (options.http.policy && NativeWebSocket) {
29978
+ globals.WebSocket = policedWebSocket(NativeWebSocket, options.http.policy);
29979
+ }
29980
+ }
28924
29981
  const globalOverlay = new Proxy(globalThis, {
28925
29982
  get: (target, key) => typeof key === "string" && key in globals ? globals[key] : key === "global" || key === "globalThis" ? globalOverlay : Reflect.get(target, key),
28926
29983
  has: (target, key) => typeof key === "string" && key in globals || Reflect.has(target, key)
@@ -28936,6 +29993,10 @@ function createCoreModules(options) {
28936
29993
  /** Client requests sent but not yet read to completion. */
28937
29994
  pendingRequests: () => inFlightRequests,
28938
29995
  cancelTimers: timers.cancelAll,
29996
+ reportUncaught: (error) => reportUncaught(error),
29997
+ reportUnhandledRejection: (reason) => reportUncaught(reason, "unhandledRejection"),
29998
+ exitStatus: () => normalizeExitCode(processObject.exitCode),
29999
+ emitExit,
28939
30000
  writeStdin: (data) => {
28940
30001
  if (options.interactiveStdin) stdin.write(data);
28941
30002
  },
@@ -28997,7 +30058,7 @@ function createPathModule(cwd) {
28997
30058
  path.default = path;
28998
30059
  return path;
28999
30060
  }
29000
- function createFsModule(volume, cwd, stdinPath) {
30061
+ function createFsModule(volume, cwd, stdinPath, defer = queueMicrotask) {
29001
30062
  const fds = /* @__PURE__ */ new Map();
29002
30063
  let nextFd = 3;
29003
30064
  if (stdinPath) fds.set(0, { path: stdinPath, position: 0, flags: "r" });
@@ -29037,12 +30098,15 @@ function createFsModule(volume, cwd, stdinPath) {
29037
30098
  volume.rmdirSync(path);
29038
30099
  };
29039
30100
  const callback = (operation, cb) => {
29040
- queueMicrotask(() => {
30101
+ defer(() => {
30102
+ let value;
29041
30103
  try {
29042
- cb(null, operation());
30104
+ value = operation();
29043
30105
  } catch (error) {
29044
30106
  cb(error);
30107
+ return;
29045
30108
  }
30109
+ cb(null, value);
29046
30110
  });
29047
30111
  };
29048
30112
  const fs = {
@@ -29172,7 +30236,7 @@ function createFsModule(volume, cwd, stdinPath) {
29172
30236
  const slice = whole.subarray(start2, Math.max(start2, end));
29173
30237
  const stream = streamModule4__default.default.Readable.from([settings.encoding ? slice.toString(settings.encoding) : slice]);
29174
30238
  Object.assign(stream, { path: target, bytesRead: slice.length, close: () => stream.destroy() });
29175
- queueMicrotask(() => {
30239
+ defer(() => {
29176
30240
  stream.emit("open", 0);
29177
30241
  stream.emit("ready");
29178
30242
  });
@@ -29240,7 +30304,7 @@ function createFsModule(volume, cwd, stdinPath) {
29240
30304
  }
29241
30305
  fs.realpath.native = fs.realpath;
29242
30306
  fs.exists = (path, cb) => {
29243
- queueMicrotask(() => cb(fs.existsSync(path)));
30307
+ defer(() => cb(fs.existsSync(path)));
29244
30308
  };
29245
30309
  fs.promises = {
29246
30310
  ...Object.fromEntries(
@@ -29362,7 +30426,7 @@ var Console = class {
29362
30426
  timeLog = (_label = "default") => {
29363
30427
  };
29364
30428
  };
29365
- function createDnsModule() {
30429
+ function createDnsModule(defer = queueMicrotask) {
29366
30430
  const LOOPBACK = {
29367
30431
  localhost: { address: "127.0.0.1", family: 4 },
29368
30432
  "127.0.0.1": { address: "127.0.0.1", family: 4 },
@@ -29382,7 +30446,7 @@ function createDnsModule() {
29382
30446
  const lookup = (hostname, options, callback) => {
29383
30447
  const done = typeof options === "function" ? options : callback;
29384
30448
  const all = typeof options === "object" && options !== null && options.all;
29385
- queueMicrotask(() => {
30449
+ defer(() => {
29386
30450
  try {
29387
30451
  const entry = lookupSync(hostname);
29388
30452
  done?.(null, all ? [entry] : entry.address, entry.family);
@@ -29540,7 +30604,16 @@ var ERRNO_CONSTANTS = {
29540
30604
  EWOULDBLOCK: 11,
29541
30605
  EXDEV: 18
29542
30606
  };
29543
- function createTrackedTimers() {
30607
+ function createTrackedTimers(onError = (error) => {
30608
+ throw error;
30609
+ }) {
30610
+ const run2 = (fn, args) => {
30611
+ try {
30612
+ fn(...args);
30613
+ } catch (error) {
30614
+ onError(error);
30615
+ }
30616
+ };
29544
30617
  const live = /* @__PURE__ */ new Set();
29545
30618
  const unrefed = /* @__PURE__ */ new Set();
29546
30619
  const states = /* @__PURE__ */ new WeakMap();
@@ -29583,7 +30656,7 @@ function createTrackedTimers() {
29583
30656
  const native = setTimeout(
29584
30657
  (...inner) => {
29585
30658
  complete(handle);
29586
- fn(...inner);
30659
+ run2(fn, inner);
29587
30660
  },
29588
30661
  delay,
29589
30662
  ...args
@@ -29591,14 +30664,14 @@ function createTrackedTimers() {
29591
30664
  handle = track(native);
29592
30665
  return handle;
29593
30666
  };
29594
- const setIntervalTracked = (fn, delay, ...args) => track(setInterval(fn, delay, ...args));
30667
+ const setIntervalTracked = (fn, delay, ...args) => track(setInterval((...inner) => run2(fn, inner), delay, ...args));
29595
30668
  const hostSetImmediate = globalThis.setImmediate;
29596
30669
  const setImmediateTracked = (fn, ...args) => {
29597
30670
  if (!hostSetImmediate) return setTimeoutTracked(fn, 0, ...args);
29598
30671
  let handle;
29599
30672
  const native = hostSetImmediate((...inner) => {
29600
30673
  complete(handle);
29601
- fn(...inner);
30674
+ run2(fn, inner);
29602
30675
  }, ...args);
29603
30676
  handle = track(native);
29604
30677
  return handle;
@@ -29781,6 +30854,43 @@ function createNetModule() {
29781
30854
  isIP: (value) => isIPv4(value) ? 4 : isIPv6(value) ? 6 : 0
29782
30855
  });
29783
30856
  }
30857
+ function parseTestSettings(raw) {
30858
+ if (!raw) return {};
30859
+ try {
30860
+ const parsed = JSON.parse(raw);
30861
+ return parsed && typeof parsed === "object" ? parsed : {};
30862
+ } catch {
30863
+ return {};
30864
+ }
30865
+ }
30866
+ var EVENT_EMITTER_METHODS = [
30867
+ "on",
30868
+ "addListener",
30869
+ "once",
30870
+ "off",
30871
+ "removeListener",
30872
+ "removeAllListeners",
30873
+ "emit",
30874
+ "prependListener",
30875
+ "prependOnceListener",
30876
+ "listeners",
30877
+ "rawListeners",
30878
+ "listenerCount",
30879
+ "eventNames",
30880
+ "setMaxListeners",
30881
+ "getMaxListeners"
30882
+ ];
30883
+ function normalizeExitCode(value) {
30884
+ const code = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
30885
+ return typeof code === "number" && Number.isInteger(code) ? code & 255 : 0;
30886
+ }
30887
+ function formatUncaught(error) {
30888
+ const text2 = error instanceof Error ? error.stack ?? `${error.name}: ${error.message}` : `Uncaught ${typeof error === "string" ? JSON.stringify(error) : String(error)}`;
30889
+ return `${text2}
30890
+
30891
+ Node.js v22.12.0
30892
+ `;
30893
+ }
29784
30894
  var stubNames = ["cluster", "dgram", "diagnostics_channel", "domain", "http2", "inspector", "net", "tls", "v8", "vm", "worker_threads"];
29785
30895
  var builtinNames2 = [
29786
30896
  "assert",
@@ -29818,6 +30928,7 @@ var builtinNames2 = [
29818
30928
  "util",
29819
30929
  "util/types",
29820
30930
  "zlib",
30931
+ "test",
29821
30932
  ...stubNames
29822
30933
  ];
29823
30934
 
@@ -30639,16 +31750,20 @@ async function loadNodeRolldownMemfsBinding() {
30639
31750
  return null;
30640
31751
  }
30641
31752
  const fromBinding = createRequire(join3(packageDir, "package.json"));
30642
- let runtime;
31753
+ let runtimeModuleUrl2;
30643
31754
  let fsModuleUrl;
30644
- let createContext2;
30645
31755
  try {
30646
- runtime = fromBinding("@napi-rs/wasm-runtime");
31756
+ runtimeModuleUrl2 = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", ".");
30647
31757
  fsModuleUrl = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", "./fs");
30648
- ({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
30649
31758
  } catch {
30650
31759
  return null;
30651
31760
  }
31761
+ const runtime = await import(
31762
+ /* @vite-ignore */
31763
+ /* webpackIgnore: true */
31764
+ runtimeModuleUrl2
31765
+ );
31766
+ const createContext2 = runtime.createContext;
30652
31767
  const { memfs } = await import(
30653
31768
  /* @vite-ignore */
30654
31769
  /* webpackIgnore: true */
@@ -30680,7 +31795,7 @@ async function loadNodeRolldownMemfsBinding() {
30680
31795
  onCreateWorker() {
30681
31796
  const worker = new Worker2(workerPath, {
30682
31797
  env: process.env,
30683
- workerData: { runtimeAnchor, fsModuleUrl },
31798
+ workerData: { runtimeAnchor, runtimeModuleUrl: runtimeModuleUrl2, fsModuleUrl },
30684
31799
  /*
30685
31800
  * Deliberately no `execArgv`.
30686
31801
  *
@@ -31123,6 +32238,8 @@ var LocalRuntimePod = class _LocalRuntimePod {
31123
32238
  rolldownBinding;
31124
32239
  /** Backs outbound `http`/`https` client requests from inside the sandbox. */
31125
32240
  fetch;
32241
+ /** What guest programs may reach outside the container. */
32242
+ networkPolicy;
31126
32243
  constructor(options) {
31127
32244
  ensureProcessGlobal();
31128
32245
  this.sockets = new VirtualTcpNetwork((port) => this.router.activePortsIncludes(port));
@@ -31140,6 +32257,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
31140
32257
  this.router.onListen = (port) => notify(port, `http://localhost:${port}`);
31141
32258
  }
31142
32259
  if (options.fetch) this.fetch = options.fetch;
32260
+ if (options.network) this.networkPolicy = { ...options.network };
31143
32261
  this.packages = new CleanPackageInstaller(this.volume, {
31144
32262
  cwd: this.workdir,
31145
32263
  ...options.registry ? { registry: options.registry } : {},
@@ -31150,6 +32268,10 @@ var LocalRuntimePod = class _LocalRuntimePod {
31150
32268
  static async boot(options = {}) {
31151
32269
  return new _LocalRuntimePod(options);
31152
32270
  }
32271
+ /** Apply a container's outbound policy to programs started from now on. */
32272
+ setNetworkPolicy(policy) {
32273
+ this.networkPolicy = { ...policy };
32274
+ }
31153
32275
  async spawn(command, args = [], options = {}) {
31154
32276
  this.assertActive();
31155
32277
  if (command !== "node" && command !== "nodejs") throw commandError(command);
@@ -31162,7 +32284,6 @@ var LocalRuntimePod = class _LocalRuntimePod {
31162
32284
  await this.prepareRolldown(cwd, env2);
31163
32285
  if (proc.isKilled()) return 137;
31164
32286
  const untrack = trackProcess(proc);
31165
- let requestedExit = 0;
31166
32287
  let engine;
31167
32288
  const core = createCoreModules({
31168
32289
  volume: this.volume,
@@ -31176,11 +32297,15 @@ var LocalRuntimePod = class _LocalRuntimePod {
31176
32297
  * thrown from a later callback it would escape into whichever library
31177
32298
  * scheduled that callback and look like an unrelated crash. */
31178
32299
  onExit: (code) => {
31179
- requestedExit = code;
31180
32300
  proc.exitNow(code);
31181
32301
  if (engine?.isEvaluating) throw new ProcessExit(code);
31182
32302
  },
31183
- http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
32303
+ http: {
32304
+ router: this.router,
32305
+ owner,
32306
+ ...this.fetch ? { fetch: this.fetch } : {},
32307
+ ...this.networkPolicy ? { policy: this.networkPolicy } : {}
32308
+ },
31184
32309
  spawnChild: (config2) => this.processManager.spawn(config2),
31185
32310
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
31186
32311
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
@@ -31204,7 +32329,9 @@ var LocalRuntimePod = class _LocalRuntimePod {
31204
32329
  try {
31205
32330
  await Promise.race([engine.run(script), proc.waitForKill()]);
31206
32331
  await this.settle(owner, core.pendingHandles, core.readingStdin, () => core.pendingRequests() + hostWork.pending(), () => proc.isKilled());
31207
- return requestedExit;
32332
+ const status = core.exitStatus();
32333
+ core.emitExit(status);
32334
+ return status;
31208
32335
  } finally {
31209
32336
  hostWork.dispose();
31210
32337
  core.cancelTimers();
@@ -31707,6 +32834,9 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31707
32834
  case "http-response":
31708
32835
  this.settleProxied(Number(message.id), message.response);
31709
32836
  return;
32837
+ case "loopback-request":
32838
+ void this.answerLoopback(worker, message);
32839
+ return;
31710
32840
  case "ws-data":
31711
32841
  this.upgraded.get(Number(message.id))?.write(new Uint8Array(message.data));
31712
32842
  return;
@@ -31742,7 +32872,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31742
32872
  aliases: this.aliases,
31743
32873
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
31744
32874
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
31745
- ...options.tty ? { tty: true } : {}
32875
+ ...options.tty ? { tty: true } : {},
32876
+ ...this.networkPolicy ? { network: this.networkPolicy } : {}
31746
32877
  });
31747
32878
  process2.begin();
31748
32879
  return process2;
@@ -31878,6 +33009,38 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31878
33009
  init: { method: request.method, path: request.url, headers: request.headers }
31879
33010
  });
31880
33011
  }
33012
+ /**
33013
+ * Answer a program that is calling a server in this container.
33014
+ *
33015
+ * A guest's router holds only the servers on its own thread, so a request to
33016
+ * a port some other process opened comes here, where every server in the
33017
+ * container is registered. `null` means nothing listens, which the guest
33018
+ * reports as a refused connection.
33019
+ */
33020
+ async answerLoopback(worker, message) {
33021
+ const port = Number(message.port);
33022
+ let response = null;
33023
+ if (this.router.activePortsIncludes(port)) {
33024
+ try {
33025
+ const result = await this.router.request(port, message.request);
33026
+ const body = result.body;
33027
+ response = {
33028
+ statusCode: result.statusCode,
33029
+ statusMessage: result.statusMessage,
33030
+ headers: result.headers,
33031
+ body: typeof body === "string" ? new TextEncoder().encode(body) : body instanceof ArrayBuffer ? new Uint8Array(body) : body ?? new Uint8Array()
33032
+ };
33033
+ } catch (error) {
33034
+ response = {
33035
+ statusCode: 502,
33036
+ statusMessage: "Bad Gateway",
33037
+ headers: {},
33038
+ body: new TextEncoder().encode(error instanceof Error ? error.message : String(error))
33039
+ };
33040
+ }
33041
+ }
33042
+ worker.postMessage({ type: "loopback-response", id: message.id, response });
33043
+ }
31881
33044
  settleProxied(id, response) {
31882
33045
  const resolve3 = this.waiting.get(id);
31883
33046
  if (!resolve3) return;
@@ -31968,13 +33131,19 @@ var Container = class _Container {
31968
33131
  // ── boot ──────────────────────────────────────────────────────────────────
31969
33132
  static async create(opts = {}) {
31970
33133
  if (opts.python) configurePython(opts.python);
33134
+ const network = {
33135
+ allowOutbound: opts.network?.allowOutbound ?? false,
33136
+ allowedHosts: opts.network?.allowedHosts ?? null
33137
+ };
31971
33138
  const podOptions = {
31972
33139
  workdir: opts.cwd ?? "/",
31973
33140
  env: opts.env ?? {},
33141
+ network,
31974
33142
  ...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
31975
33143
  };
31976
33144
  const workerOptions = { ...podOptions, ...opts.workerUrl ? { workerUrl: opts.workerUrl } : {} };
31977
33145
  const pod = opts.pod ?? (opts.isolation === "realm" ? null : opts.isolation === "worker" ? await WorkerRuntimePod.boot(workerOptions) : await WorkerRuntimePod.tryBoot(workerOptions, opts.onRuntimeFallback)) ?? await LocalRuntimePod.boot(podOptions);
33146
+ if (opts.pod) opts.pod.setNetworkPolicy?.(network);
31978
33147
  const kernel = new Kernel({
31979
33148
  pod,
31980
33149
  hostname: opts.hostname ?? "sandbox",
@@ -32069,7 +33238,7 @@ var Container = class _Container {
32069
33238
  async exec(command, opts = {}) {
32070
33239
  this.assertActive();
32071
33240
  const started = Date.now();
32072
- const { io, stdout, stderr, combined } = this.makeStdio(opts);
33241
+ const { io, stdout, stderr, combined, detach } = this.makeStdio(opts);
32073
33242
  const cred = opts.user ? this.kernel.users.credFor(opts.user) : this.defaults.cred;
32074
33243
  const env2 = { ...this.defaults.env, ...opts.env };
32075
33244
  const cwd = opts.cwd ? resolve(this.defaults.cwd, opts.cwd) : this.defaults.cwd;
@@ -32098,6 +33267,7 @@ var Container = class _Container {
32098
33267
  ]);
32099
33268
  } finally {
32100
33269
  if (timer) clearTimeout(timer);
33270
+ detach();
32101
33271
  proc.exit(0);
32102
33272
  this.kernel.procs.remove(proc.pid);
32103
33273
  }
@@ -32114,7 +33284,7 @@ var Container = class _Container {
32114
33284
  async run(argv, opts = {}) {
32115
33285
  this.assertActive();
32116
33286
  const started = Date.now();
32117
- const { io, stdout, stderr, combined } = this.makeStdio(opts);
33287
+ const { io, stdout, stderr, combined, detach } = this.makeStdio(opts);
32118
33288
  const cred = opts.user ? this.kernel.users.credFor(opts.user) : this.defaults.cred;
32119
33289
  const cwd = opts.cwd ? resolve(this.defaults.cwd, opts.cwd) : this.defaults.cwd;
32120
33290
  const result = await this.kernel.run(argv, {
@@ -32126,6 +33296,7 @@ var Container = class _Container {
32126
33296
  stderr: io.stderr,
32127
33297
  ...opts.timeoutMs ?? this.defaults.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs ?? this.defaults.timeoutMs } : {}
32128
33298
  });
33299
+ detach();
32129
33300
  return {
32130
33301
  exitCode: result.exitCode,
32131
33302
  stdout: stdout.text(),
@@ -32216,6 +33387,14 @@ var Container = class _Container {
32216
33387
  if (!this.defaultSession) this.defaultSession = this.session();
32217
33388
  return this.defaultSession;
32218
33389
  }
33390
+ /**
33391
+ * The streams one call collects into.
33392
+ *
33393
+ * They are detachable because a background job started by the command keeps
33394
+ * writing after the call has returned. Nothing reads the result any more by
33395
+ * then, so without `detach` a chatty server would grow the buffer for as long
33396
+ * as it ran; afterwards its output still reaches the container-wide taps.
33397
+ */
32219
33398
  makeStdio(opts) {
32220
33399
  const combined = [];
32221
33400
  const decoder9 = new TextDecoder();
@@ -32231,14 +33410,25 @@ var Container = class _Container {
32231
33410
  opts.onStderr?.(text2);
32232
33411
  this.hooks.onStderr?.(text2);
32233
33412
  });
33413
+ const outGate = new DetachableOutput(stdout, (text2) => this.hooks.onStdout?.(text2));
33414
+ const errGate = new DetachableOutput(stderr, (text2) => this.hooks.onStderr?.(text2));
32234
33415
  if (opts.tty) {
32235
- stdout.isTTY = true;
32236
- stderr.isTTY = true;
32237
- stdout.columns = opts.columns ?? 80;
32238
- stdout.rows = opts.rows ?? 24;
33416
+ outGate.isTTY = true;
33417
+ errGate.isTTY = true;
33418
+ outGate.columns = opts.columns ?? 80;
33419
+ outGate.rows = opts.rows ?? 24;
32239
33420
  }
32240
33421
  const stdin = opts.stdin === void 0 ? new NullInput() : Pipe.from(opts.stdin);
32241
- return { io: { stdin, stdout, stderr }, stdout, stderr, combined };
33422
+ return {
33423
+ io: { stdin, stdout: outGate, stderr: errGate },
33424
+ stdout,
33425
+ stderr,
33426
+ combined,
33427
+ detach: () => {
33428
+ outGate.detach();
33429
+ errGate.detach();
33430
+ }
33431
+ };
32242
33432
  }
32243
33433
  // ── networking ────────────────────────────────────────────────────────────
32244
33434
  /** Send an HTTP request to a server running inside the container. */