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.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { inflateRaw, ungzip, gzip as gzip$1, deflate as deflate$1, inflate as inflate$1, deflateRaw } from 'pako';
2
+ import { Parser as Parser$1, parse as parse$1 } from 'acorn';
3
+ import jsx from 'acorn-jsx';
2
4
  import { sha256, sha224 } from '@noble/hashes/sha256';
3
5
  import { Buffer as Buffer$1 } from 'buffer/index.js';
4
6
  import { sha512, sha384 } from '@noble/hashes/sha512';
5
7
  import { sha1 } from '@noble/hashes/sha1';
6
8
  import { valid, maxSatisfying } from 'semver';
7
- import { Parser as Parser$1, parse as parse$1 } from 'acorn';
8
9
  import EventEmitter4 from 'events/events.js';
9
- import jsx from 'acorn-jsx';
10
10
  import { exports as exports$1, imports } from 'resolve.exports';
11
11
  import pathModule from 'path-browserify';
12
12
  import streamModule4 from 'stream-browserify';
@@ -3807,7 +3807,10 @@ function builtinKill({ shell, argv, io }) {
3807
3807
  for (const target of args.slice(i)) {
3808
3808
  if (target.startsWith("%")) {
3809
3809
  const job = shell.jobs.find((j) => j.id === Number(target.slice(1)));
3810
- if (job) continue;
3810
+ if (job) {
3811
+ if (job.state !== "done") shell.kernel.procs.signal(job.pgid, signal);
3812
+ continue;
3813
+ }
3811
3814
  io.stderr.write(`kill: ${target}: no such job
3812
3815
  `);
3813
3816
  status = 1;
@@ -5403,6 +5406,51 @@ var BufferSink = class {
5403
5406
  this.total = 0;
5404
5407
  }
5405
5408
  };
5409
+ var DetachableOutput = class {
5410
+ constructor(inner, onDetached) {
5411
+ this.inner = inner;
5412
+ this.onDetached = onDetached;
5413
+ }
5414
+ inner;
5415
+ onDetached;
5416
+ attached = true;
5417
+ decoder = new TextDecoder();
5418
+ get closed() {
5419
+ return this.attached ? this.inner.closed : false;
5420
+ }
5421
+ get isTTY() {
5422
+ return this.inner.isTTY;
5423
+ }
5424
+ set isTTY(value) {
5425
+ this.inner.isTTY = value;
5426
+ }
5427
+ get columns() {
5428
+ return this.inner.columns;
5429
+ }
5430
+ set columns(value) {
5431
+ this.inner.columns = value;
5432
+ }
5433
+ get rows() {
5434
+ return this.inner.rows;
5435
+ }
5436
+ set rows(value) {
5437
+ this.inner.rows = value;
5438
+ }
5439
+ write(data) {
5440
+ if (this.attached) {
5441
+ this.inner.write(data);
5442
+ return;
5443
+ }
5444
+ if (!this.onDetached) return;
5445
+ this.onDetached(typeof data === "string" ? data : this.decoder.decode(data, { stream: true }));
5446
+ }
5447
+ end() {
5448
+ if (this.attached) this.inner.end();
5449
+ }
5450
+ detach() {
5451
+ this.attached = false;
5452
+ }
5453
+ };
5406
5454
  var CallbackSink = class {
5407
5455
  constructor(sink, opts = {}) {
5408
5456
  this.sink = sink;
@@ -7265,6 +7313,77 @@ function createSysProvider(kernel) {
7265
7313
  };
7266
7314
  }
7267
7315
 
7316
+ // src/net/policy.ts
7317
+ function isLoopbackHostname(hostname) {
7318
+ const host2 = hostname.replace(/^\[|\]$/g, "").toLowerCase();
7319
+ return host2 === "localhost" || host2 === "0.0.0.0" || host2 === "::1" || host2 === "::" || host2.startsWith("127.") || host2.endsWith(".localhost");
7320
+ }
7321
+ function outboundAllowed(policy, url) {
7322
+ if (!policy.allowOutbound) return false;
7323
+ let hostname;
7324
+ try {
7325
+ hostname = new URL(url).hostname;
7326
+ } catch {
7327
+ return false;
7328
+ }
7329
+ if (isLoopbackHostname(hostname)) return false;
7330
+ const hosts = policy.allowedHosts;
7331
+ if (!hosts) return true;
7332
+ return hosts.some((allowed) => hostname === allowed || hostname.endsWith(`.${allowed}`));
7333
+ }
7334
+ function outboundBlockedError(url) {
7335
+ let host2 = url;
7336
+ try {
7337
+ host2 = new URL(url).host;
7338
+ } catch {
7339
+ }
7340
+ return Object.assign(
7341
+ new Error(
7342
+ `connect ENETUNREACH ${host2}: outbound network access is disabled for this container (allowed by the host with network.allowOutbound and network.allowedHosts)`
7343
+ ),
7344
+ { code: "ENETUNREACH", errno: -101, syscall: "connect", hostname: host2 }
7345
+ );
7346
+ }
7347
+ function requestUrl(input) {
7348
+ if (typeof input === "string") return input;
7349
+ if (input instanceof URL) return input.href;
7350
+ return input.url;
7351
+ }
7352
+ function policedFetch(policy, fetchImpl) {
7353
+ return (async (input, init) => {
7354
+ const url = requestUrl(input);
7355
+ if (!outboundAllowed(policy, url)) {
7356
+ throw Object.assign(new TypeError("fetch failed"), { cause: outboundBlockedError(url) });
7357
+ }
7358
+ return fetchImpl(input, init);
7359
+ });
7360
+ }
7361
+ function policedWebSocket(Native, policy) {
7362
+ const Base = Native;
7363
+ return class PolicedWebSocket extends Base {
7364
+ constructor(url, protocols) {
7365
+ const href = url instanceof URL ? url.href : String(url);
7366
+ let parsed = null;
7367
+ try {
7368
+ parsed = new URL(href);
7369
+ } catch {
7370
+ }
7371
+ if (parsed && isLoopbackHostname(parsed.hostname)) {
7372
+ throw Object.assign(
7373
+ new Error(
7374
+ `WebSocket to ${parsed.host} is not available from a program inside the container; reach servers in the container over HTTP`
7375
+ ),
7376
+ { code: "ERR_UNSUPPORTED" }
7377
+ );
7378
+ }
7379
+ if (parsed && !outboundAllowed(policy, href.replace(/^ws(s?):/i, "http$1:"))) {
7380
+ throw outboundBlockedError(href);
7381
+ }
7382
+ super(url, protocols);
7383
+ }
7384
+ };
7385
+ }
7386
+
7268
7387
  // src/net/stack.ts
7269
7388
  var NetworkStack = class {
7270
7389
  constructor(pod, vfs, options = {}) {
@@ -7403,16 +7522,12 @@ var NetworkStack = class {
7403
7522
  return false;
7404
7523
  }
7405
7524
  // ── outbound policy ──────────────────────────────────────────────────────
7525
+ /** The policy every way out of the container applies, not only the shell's. */
7526
+ get policy() {
7527
+ return { allowOutbound: this.options.allowOutbound, allowedHosts: this.options.allowedHosts ?? null };
7528
+ }
7406
7529
  outboundAllowed(url) {
7407
- if (!this.options.allowOutbound) return false;
7408
- const hosts = this.options.allowedHosts;
7409
- if (hosts === null || hosts === void 0) return true;
7410
- try {
7411
- const { hostname } = new URL(url);
7412
- return hosts.some((h) => hostname === h || hostname.endsWith("." + h));
7413
- } catch {
7414
- return false;
7415
- }
7530
+ return outboundAllowed(this.policy, url);
7416
7531
  }
7417
7532
  // ── /proc plumbing ───────────────────────────────────────────────────────
7418
7533
  procNetDev() {
@@ -8560,11 +8675,16 @@ var Shell = class _Shell {
8560
8675
  }
8561
8676
  return path;
8562
8677
  }
8563
- /** A copy that shares nothing mutable with this shell. */
8564
- fork() {
8678
+ /**
8679
+ * A copy that shares nothing mutable with this shell.
8680
+ *
8681
+ * Subshells and pipeline stages run as part of this shell's process. A
8682
+ * background job passes a process of its own, the way fork(2) gives one.
8683
+ */
8684
+ fork(proc = this.proc) {
8565
8685
  const sub = new _Shell({
8566
8686
  kernel: this.kernel,
8567
- proc: this.proc,
8687
+ proc,
8568
8688
  cwd: this.cwd,
8569
8689
  cred: this.cred,
8570
8690
  env: {},
@@ -8719,29 +8839,42 @@ sys ${fmt2(Math.floor(ms * 0.2))}
8719
8839
  `);
8720
8840
  }
8721
8841
  startBackgroundJob(node2, io) {
8722
- const sub = this.fork();
8842
+ const jobProc = this.kernel.procs.create({
8843
+ argv: [this.scriptName],
8844
+ cwd: this.cwd,
8845
+ env: this.vars.environment(),
8846
+ cred: this.cred,
8847
+ ppid: this.proc.pid,
8848
+ kind: "shell",
8849
+ tty: this.proc.tty,
8850
+ stdio: { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr }
8851
+ });
8852
+ const sub = this.fork(jobProc);
8723
8853
  const id = this.nextJobId++;
8724
8854
  const command = describeNode(node2);
8725
8855
  const job = {
8726
8856
  id,
8727
- pgid: this.proc.pid + id,
8857
+ pgid: jobProc.pid,
8728
8858
  command,
8729
8859
  state: "running",
8730
8860
  exitCode: null,
8731
- pids: [],
8861
+ pids: [jobProc.pid],
8732
8862
  promise: Promise.resolve(0)
8733
8863
  };
8734
8864
  job.promise = (async () => {
8865
+ let code;
8735
8866
  try {
8736
- const code = await sub.run(node2, { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr });
8737
- job.exitCode = code;
8738
- job.state = "done";
8739
- return code;
8867
+ code = await sub.run(node2, { stdin: new NullInput(), stdout: io.stdout, stderr: io.stderr });
8740
8868
  } catch (e) {
8741
- job.state = "done";
8742
- job.exitCode = e instanceof ShellExit ? e.code : 1;
8743
- return job.exitCode;
8869
+ code = e instanceof ShellExit ? e.code : 1;
8744
8870
  }
8871
+ jobProc.exit(code);
8872
+ job.exitCode = jobProc.exitCode ?? code;
8873
+ job.state = "done";
8874
+ queueMicrotask(() => {
8875
+ if (jobProc.children.size === 0) this.kernel.procs.remove(jobProc.pid);
8876
+ });
8877
+ return job.exitCode;
8745
8878
  })();
8746
8879
  this.jobs.push(job);
8747
8880
  this.lastBackgroundPid = job.pgid;
@@ -17478,439 +17611,1112 @@ var commands11 = [xsel, xclip, pbcopy, pbpaste, wlCopy, wlPaste];
17478
17611
 
17479
17612
  // src/node/node.ts
17480
17613
  init_path();
17481
- var NODE_VERSION = "v22.12.0";
17482
- new TextEncoder();
17483
- var tempCounter = 0;
17484
- function tempPath(kind, pid, ext = ".js") {
17485
- return `/tmp/.sbx-${kind}-${pid}-${tempCounter++}${ext}`;
17486
- }
17487
- function withPrelude(source, prelude) {
17488
- if (!source.startsWith("#!")) return prelude + source;
17489
- const eol = source.indexOf("\n");
17490
- if (eol < 0) return source + "\n" + prelude;
17491
- return source.slice(0, eol + 1) + prelude + source.slice(eol + 1);
17492
- }
17493
- function siblingPath(script, pid) {
17494
- const ext = extname(script) || ".js";
17495
- return join(dirname(script), `.sbx-${pid}-${tempCounter++}-${basename(script, ext)}${ext}`);
17496
- }
17497
- var BUFFER_COMPAT_SHIM = `
17498
- (function () {
17499
- const seen = new Set();
17500
- const widen = (B) => {
17501
- if (!B || seen.has(B) || typeof B.isBuffer !== 'function') return;
17502
- seen.add(B);
17503
- const original = B.isBuffer.bind(B);
17504
- B.isBuffer = (value) => original(value) || value instanceof Uint8Array;
17505
- };
17506
- try { widen(globalThis.Buffer); } catch (e) {}
17507
- try { widen(require('buffer').Buffer); } catch (e) {}
17508
- })();
17509
- `;
17510
- function buildPrelude(scriptPath, argv, stdinPath) {
17511
- const argvLiteral = JSON.stringify(["/usr/bin/node", scriptPath, ...argv]);
17512
- const pathLiteral = JSON.stringify(scriptPath);
17513
- const stdinLiteral = JSON.stringify(stdinPath);
17514
- return `${BUFFER_COMPAT_SHIM}
17515
- process.argv = ${argvLiteral};
17516
- try {
17517
- __filename = ${pathLiteral};
17518
- module.filename = ${pathLiteral};
17519
- module.id = ${pathLiteral};
17520
- if (require.main === module) require.main.filename = ${pathLiteral};
17521
- } catch (e) {}
17522
- ${stdinPath === null ? "" : `(function () {
17523
- const fs = require('fs');
17524
- const { Readable } = require('stream');
17525
- let data;
17526
- try { data = fs.readFileSync(${stdinLiteral}); } catch (e) { data = Buffer.alloc(0); }
17527
- const stream = Readable.from([data]);
17528
- stream.isTTY = false;
17529
- stream.fd = 0;
17530
- stream.setRawMode = function () { return this; };
17531
- try {
17532
- Object.defineProperty(process, 'stdin', { value: stream, configurable: true, writable: true });
17533
- } catch (e) { process.stdin = stream; }
17534
- })();
17535
- `}`;
17614
+ var HELPERS = {
17615
+ /** Import a specifier and return an ES-module-shaped namespace. */
17616
+ import: "__sbxImport",
17617
+ /** `import(...)`, returning a promise of a namespace. */
17618
+ dynamic: "__sbxDynamicImport",
17619
+ /** `export * from` — copy live bindings onto `exports`. */
17620
+ exportAll: "__sbxExportAll",
17621
+ /** `import.meta`. */
17622
+ meta: "__sbxMeta",
17623
+ /**
17624
+ * The exports object, under a name of the engine's choosing.
17625
+ *
17626
+ * An ES module is free to declare its own top-level `exports`, `require` or
17627
+ * `__dirname` they are ordinary identifiers there, and real packages use
17628
+ * all three (`const require = createRequire(import.meta.url)` is close to
17629
+ * idiomatic). Naming the wrapper's binding something no source would write
17630
+ * removes that entire class of collision instead of patching it up after a
17631
+ * `SyntaxError`.
17632
+ */
17633
+ exports: "__sbxExports"
17634
+ };
17635
+ var DEFAULT_LOCAL = "__sbxDefault";
17636
+ var MAYBE_ESM = /(^|[\s;}(])(?:import|export)(?:[\s({[*"']|$)|\bimport\s*\.\s*meta\b/;
17637
+ function looksLikeEsm(source) {
17638
+ return MAYBE_ESM.test(source);
17536
17639
  }
17537
- async function execute(ctx, invocation) {
17538
- const { pod } = ctx.kernel;
17539
- const live = ctx.stdin.interactive || ctx.stdin.isTTY;
17540
- const tty = ctx.stdin.isTTY;
17541
- let exitCode = 0;
17640
+ function transformEsm(source, filename = "module.js") {
17641
+ if (!looksLikeEsm(source)) return null;
17642
+ let ast;
17542
17643
  try {
17543
- const proc = await pod.spawn("node", [invocation.script], {
17544
- cwd: ctx.cwd,
17545
- env: { ...ctx.env, PWD: ctx.cwd },
17546
- argv: ["/usr/bin/node", invocation.argvPath ?? invocation.script, ...invocation.argv],
17547
- ...invocation.stdinPath ? { stdinPath: invocation.stdinPath } : {},
17548
- /* An interactive caller's input was not captured to a file, so the
17549
- * program's stdin has to stay open and be fed as it arrives — and be
17550
- * reported as a terminal, since that is what decides whether a CLI
17551
- * prompts or takes its defaults. */
17552
- ...live ? { interactiveStdin: true } : {},
17553
- ...tty ? { tty: true } : {}
17554
- });
17555
- proc.on("output", (chunk) => {
17556
- try {
17557
- ctx.write(chunk);
17558
- } catch {
17559
- }
17560
- });
17561
- proc.on("error", (chunk) => {
17562
- try {
17563
- ctx.stderr.write(chunk);
17564
- } catch {
17565
- }
17566
- });
17567
- proc.on("rawmode", (enabled) => {
17568
- ctx.stdin.rawMode = enabled;
17569
- });
17570
- let forwarding = false;
17571
- if (live) {
17572
- forwarding = true;
17573
- void (async () => {
17574
- while (forwarding) {
17575
- const chunk = await ctx.stdin.read();
17576
- if (chunk === null) {
17577
- proc.endInput?.();
17578
- break;
17579
- }
17580
- try {
17581
- proc.write(new TextDecoder().decode(chunk));
17582
- } catch {
17583
- break;
17584
- }
17585
- }
17586
- })();
17587
- }
17588
- let onAbort;
17589
- const aborted = new Promise((resolve3) => {
17590
- onAbort = () => {
17591
- try {
17592
- proc.kill();
17593
- } catch {
17594
- }
17595
- resolve3({ exitCode: 137 });
17596
- };
17597
- if (ctx.signal.aborted) onAbort();
17598
- else ctx.signal.addEventListener("abort", onAbort, { once: true });
17599
- });
17600
- const result = await Promise.race([proc.completion, aborted]);
17601
- forwarding = false;
17602
- if (onAbort) ctx.signal.removeEventListener("abort", onAbort);
17603
- exitCode = result.exitCode;
17604
- } catch (e) {
17605
- ctx.stderr.write(`node: ${e instanceof Error ? e.message : String(e)}
17606
- `);
17607
- exitCode = 1;
17608
- } finally {
17609
- for (const temp of invocation.temps) {
17610
- try {
17611
- ctx.vfs.unlink(temp);
17612
- } catch {
17613
- }
17614
- }
17644
+ ast = parseModule(source, filename);
17645
+ } catch {
17646
+ return null;
17615
17647
  }
17616
- return exitCode;
17617
- }
17618
- async function captureStdin(ctx) {
17619
- if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
17620
- const bytes2 = await ctx.stdin.readAll();
17621
- if (bytes2.length === 0) return null;
17622
- const path = tempPath("stdin", ctx.proc.pid, ".dat");
17623
- ctx.vfs.writeFile(path, bytes2, { privileged: true, mode: 384 });
17624
- return path;
17625
- }
17626
- var node = defineCommand({
17627
- name: "node",
17628
- path: "/usr/bin/node",
17629
- aliases: ["nodejs"],
17630
- summary: "run a JavaScript program with Node.js",
17631
- usage: "node [options] [script.js] [arguments]",
17632
- manual: `Runs JavaScript with a real Node.js API surface \u2014 fs, path, http,
17633
- crypto, streams, worker_threads and npm packages \u2014 inside the container.
17634
- The filesystem a script sees is the container's filesystem.`,
17635
- async run(ctx) {
17636
- const argv = ctx.args;
17637
- const temps = [];
17638
- let i = 0;
17639
- let evalSource;
17640
- let printResult = false;
17641
- let scriptArg;
17642
- for (; i < argv.length; i++) {
17643
- const arg = argv[i];
17644
- if (arg === "-v" || arg === "--version") {
17645
- ctx.line(NODE_VERSION);
17646
- return 0;
17647
- }
17648
- if (arg === "-h" || arg === "--help") {
17649
- printHelp(ctx);
17650
- return 0;
17651
- }
17652
- if (arg === "-e" || arg === "--eval") {
17653
- evalSource = argv[++i] ?? "";
17654
- i++;
17655
- break;
17656
- }
17657
- if (arg === "-p" || arg === "--print") {
17658
- printResult = true;
17659
- evalSource = argv[++i] ?? "";
17660
- i++;
17661
- break;
17662
- }
17663
- if (arg.startsWith("--eval=")) {
17664
- evalSource = arg.slice("--eval=".length);
17665
- i++;
17648
+ const body = ast.body;
17649
+ const hasModuleSyntax = body.some(
17650
+ (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
17651
+ );
17652
+ const usesImportMeta = !hasModuleSyntax && containsImportMeta(ast);
17653
+ const esm = hasModuleSyntax || usesImportMeta;
17654
+ if (!esm && !containsImportExpression(ast)) return null;
17655
+ const edits = [];
17656
+ const prelude = [];
17657
+ const importBindings = /* @__PURE__ */ new Map();
17658
+ const exportGetters = /* @__PURE__ */ new Map();
17659
+ let namespaceCount = 0;
17660
+ const namespaceFor = (specifier) => {
17661
+ const id = `__sbxNs${namespaceCount++}`;
17662
+ prelude.push(`var ${id} = ${HELPERS.import}(${JSON.stringify(specifier)});`);
17663
+ return id;
17664
+ };
17665
+ for (const node2 of body) {
17666
+ if (node2.type === "ImportDeclaration") collectImport(node2);
17667
+ }
17668
+ for (const node2 of body) {
17669
+ switch (node2.type) {
17670
+ case "ExportNamedDeclaration":
17671
+ collectNamedExport(node2);
17666
17672
  break;
17667
- }
17668
- if (arg.startsWith("--print=")) {
17669
- printResult = true;
17670
- evalSource = arg.slice("--print=".length);
17671
- i++;
17673
+ case "ExportDefaultDeclaration":
17674
+ collectDefaultExport(node2);
17672
17675
  break;
17673
- }
17674
- if (arg === "--") {
17675
- i++;
17676
+ case "ExportAllDeclaration":
17677
+ collectExportAll(node2);
17676
17678
  break;
17677
- }
17678
- if (arg.startsWith("-") && arg !== "-") {
17679
- continue;
17680
- }
17681
- scriptArg = arg;
17682
- i++;
17683
- break;
17684
- }
17685
- const scriptArgs = argv.slice(i);
17686
- if (evalSource !== void 0) {
17687
- const body = printResult ? `const __sbxResult = (function(){ return (${evalSource}
17688
- ); })();
17689
- console.log(typeof __sbxResult === 'string' ? __sbxResult : require('util').inspect(__sbxResult));` : evalSource;
17690
- const path = tempPath("eval", ctx.proc.pid);
17691
- const stdinPath2 = await captureStdin(ctx);
17692
- if (stdinPath2) temps.push(stdinPath2);
17693
- ctx.vfs.writeFile(path, buildBootstrapForEval(body, ["/usr/bin/node", ...scriptArgs], stdinPath2), {
17694
- privileged: true,
17695
- mode: 384
17696
- });
17697
- temps.push(path);
17698
- return execute(ctx, { script: path, argv: scriptArgs, stdinPath: stdinPath2, temps });
17699
17679
  }
17700
- if (scriptArg === void 0 || scriptArg === "-") {
17701
- if (ctx.stdin.isTTY) {
17702
- ctx.line(`Welcome to Node.js ${NODE_VERSION}.`);
17703
- ctx.line('Type ".help" for more information.');
17704
- return runRepl(ctx);
17680
+ }
17681
+ rewriteReferences();
17682
+ const header = esm ? [
17683
+ `Object.defineProperty(${HELPERS.exports}, "__esModule", { value: true });`,
17684
+ ...prelude,
17685
+ ...[...exportGetters].map(
17686
+ ([name, expression]) => `Object.defineProperty(${HELPERS.exports}, ${JSON.stringify(name)}, { enumerable: true, configurable: true, get: function () { return ${expression}; } });`
17687
+ )
17688
+ ].join("\n") : "";
17689
+ return {
17690
+ code: header ? `${header}
17691
+ ${applyEdits(source, edits)}` : applyEdits(source, edits),
17692
+ esm,
17693
+ topLevelAwait: hasTopLevelAwait(ast)
17694
+ };
17695
+ function collectImport(node2) {
17696
+ const specifier = node2.source.value;
17697
+ const specifiers = node2.specifiers ?? [];
17698
+ const id = namespaceFor(specifier);
17699
+ for (const entry of specifiers) {
17700
+ const local = entry.local.name ?? "";
17701
+ if (entry.type === "ImportDefaultSpecifier") {
17702
+ importBindings.set(local, `${id}.default`);
17703
+ } else if (entry.type === "ImportNamespaceSpecifier") {
17704
+ importBindings.set(local, id);
17705
+ } else {
17706
+ const imported = entry.imported;
17707
+ const name = imported.type === "Identifier" ? imported.name : imported.value;
17708
+ importBindings.set(local, `${id}[${JSON.stringify(name)}]`);
17705
17709
  }
17706
- const source = new TextDecoder().decode(await ctx.stdin.readAll());
17707
- if (source.trim() === "") return 0;
17708
- const path = tempPath("stdin-script", ctx.proc.pid);
17709
- ctx.vfs.writeFile(path, buildBootstrapForEval(source, ["/usr/bin/node", ...scriptArgs], null), {
17710
- privileged: true,
17711
- mode: 384
17712
- });
17713
- temps.push(path);
17714
- return execute(ctx, { script: path, argv: scriptArgs, temps });
17715
17710
  }
17716
- const script = resolveScript(ctx, scriptArg);
17717
- if (!script) {
17718
- ctx.stderr.write(
17719
- `node:internal/modules/cjs/loader:1215
17720
- throw err;
17721
- ^
17722
-
17723
- Error: Cannot find module '${ctx.path(scriptArg)}'
17724
- `
17725
- );
17726
- return 1;
17711
+ drop(node2);
17712
+ }
17713
+ function collectNamedExport(node2) {
17714
+ const declaration = node2.declaration;
17715
+ if (declaration) {
17716
+ for (const name of declaredNames(declaration)) exportGetters.set(name, name);
17717
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17718
+ return;
17727
17719
  }
17728
- const stdinPath = await captureStdin(ctx);
17729
- if (stdinPath) temps.push(stdinPath);
17730
- const sibling = siblingPath(script, ctx.proc.pid);
17731
- try {
17732
- const source = ctx.vfs.readText(script, ctx.cred);
17733
- ctx.vfs.writeFile(sibling, withPrelude(source, buildPrelude(script, scriptArgs, stdinPath)), {
17734
- privileged: true,
17735
- mode: 384
17736
- });
17737
- temps.push(sibling);
17738
- return await execute(ctx, { script: sibling, argvPath: script, argv: scriptArgs, stdinPath, temps });
17739
- } catch {
17740
- return await execute(ctx, { script, argv: scriptArgs, stdinPath, temps });
17720
+ const source_ = node2.source;
17721
+ const id = source_ ? namespaceFor(source_.value) : null;
17722
+ for (const entry of node2.specifiers ?? []) {
17723
+ const local = nameOf(entry.local);
17724
+ const exported = nameOf(entry.exported);
17725
+ const read = id ? `${id}[${JSON.stringify(local)}]` : importBindings.get(local) ?? local;
17726
+ exportGetters.set(exported, read);
17741
17727
  }
17728
+ drop(node2);
17742
17729
  }
17743
- });
17744
- function buildBootstrapForEval(source, argv, stdinPath) {
17745
- const prelude = stdinPath ? `const fs = require('fs');
17746
- const { Readable } = require('stream');
17747
- let __sbxData; try { __sbxData = fs.readFileSync(${JSON.stringify(stdinPath)}); } catch (e) { __sbxData = Buffer.alloc(0); }
17748
- const __sbxStdin = Readable.from([__sbxData]);
17749
- __sbxStdin.isTTY = false;
17750
- __sbxStdin.setRawMode = function () { return this; };
17751
- try { Object.defineProperty(process, 'stdin', { value: __sbxStdin, configurable: true, writable: true }); } catch (e) { process.stdin = __sbxStdin; }
17752
- ` : "";
17753
- return `${BUFFER_COMPAT_SHIM}process.argv = ${JSON.stringify(argv)};
17754
- ${prelude}${source}
17755
- `;
17756
- }
17757
- function resolveScript(ctx, operand) {
17758
- const base2 = ctx.path(operand);
17759
- const candidates = [base2, `${base2}.js`, `${base2}.mjs`, `${base2}.cjs`, `${base2}.json`];
17760
- for (const candidate of candidates) {
17761
- try {
17762
- const st = ctx.vfs.stat(candidate, { cred: ctx.cred });
17763
- if (st.isFile()) return ctx.vfs.realpath(candidate, ctx.cred);
17764
- if (st.isDirectory()) {
17765
- const manifest = join(candidate, "package.json");
17766
- if (ctx.vfs.lexists(manifest)) {
17767
- try {
17768
- const main = JSON.parse(ctx.vfs.readText(manifest, ctx.cred)).main;
17769
- if (main) {
17770
- const mainPath = resolve(candidate, main);
17771
- if (ctx.vfs.lexists(mainPath)) return ctx.vfs.realpath(mainPath, ctx.cred);
17772
- if (ctx.vfs.lexists(mainPath + ".js")) return ctx.vfs.realpath(mainPath + ".js", ctx.cred);
17773
- }
17774
- } catch {
17775
- }
17776
- }
17777
- for (const index of ["index.js", "index.mjs", "index.cjs"]) {
17778
- const path = join(candidate, index);
17779
- if (ctx.vfs.lexists(path)) return ctx.vfs.realpath(path, ctx.cred);
17780
- }
17730
+ function collectDefaultExport(node2) {
17731
+ const declaration = node2.declaration;
17732
+ const isDeclaration = declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration";
17733
+ if (isDeclaration) {
17734
+ const named = declaration.id;
17735
+ if (named) {
17736
+ exportGetters.set("default", named.name);
17737
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17738
+ return;
17781
17739
  }
17782
- } catch {
17740
+ const keyword = declaration.type === "FunctionDeclaration" ? "function" : "class";
17741
+ const keywordEnd = source.indexOf(keyword, declaration.start) + keyword.length;
17742
+ exportGetters.set("default", DEFAULT_LOCAL);
17743
+ edits.push({ start: node2.start, end: declaration.start, text: "" });
17744
+ edits.push({ start: keywordEnd, end: keywordEnd, text: ` ${DEFAULT_LOCAL}` });
17745
+ return;
17783
17746
  }
17747
+ exportGetters.set("default", DEFAULT_LOCAL);
17748
+ edits.push({ start: node2.start, end: declaration.start, text: `var ${DEFAULT_LOCAL} = ` });
17749
+ edits.push({ start: node2.end, end: node2.end, text: ";" });
17784
17750
  }
17785
- return null;
17786
- }
17787
- async function runRepl(ctx) {
17788
- const historyPath = join(ctx.env.HOME ?? "/root", ".node_repl_history");
17789
- const statements = [];
17790
- for (; ; ) {
17791
- ctx.write("> ");
17792
- const line = await ctx.stdin.readLine();
17793
- if (line === null) {
17794
- ctx.line("");
17795
- break;
17796
- }
17797
- const trimmed = line.trim();
17798
- if (trimmed === ".exit") break;
17799
- if (trimmed === ".help") {
17800
- ctx.line(".exit Exit the REPL");
17801
- ctx.line(".help Print this help message");
17802
- ctx.line(".clear Reset the session context");
17803
- continue;
17804
- }
17805
- if (trimmed === ".clear") {
17806
- statements.length = 0;
17807
- continue;
17808
- }
17809
- if (trimmed === "") continue;
17810
- const program = [...statements, `console.log(require('util').inspect((${trimmed})))`].join(";\n");
17811
- const fallback = [...statements, trimmed].join(";\n");
17812
- const path = tempPath("repl", ctx.proc.pid);
17813
- ctx.vfs.writeFile(path, program, { privileged: true, mode: 384 });
17814
- const result = await runQuiet(ctx, path);
17815
- if (result.exitCode !== 0) {
17816
- ctx.vfs.writeFile(path, fallback, { privileged: true, mode: 384 });
17817
- const second = await runQuiet(ctx, path);
17818
- ctx.write(second.stdout);
17819
- if (second.exitCode === 0) statements.push(trimmed);
17820
- else ctx.stderr.write(second.stderr);
17821
- } else {
17822
- ctx.write(result.stdout);
17823
- statements.push(trimmed);
17824
- }
17825
- try {
17826
- ctx.vfs.unlink(path);
17827
- } catch {
17751
+ function collectExportAll(node2) {
17752
+ const id = namespaceFor(node2.source.value);
17753
+ const exported = node2.exported;
17754
+ if (exported) exportGetters.set(nameOf(exported), id);
17755
+ else prelude.push(`${HELPERS.exportAll}(${HELPERS.exports}, ${id});`);
17756
+ drop(node2);
17757
+ }
17758
+ function drop(node2) {
17759
+ edits.push({ start: node2.start, end: node2.end, text: "" });
17760
+ }
17761
+ function rewriteReferences() {
17762
+ const programScope = { names: new Set(importBindings.keys()), parent: null };
17763
+ visit(ast, programScope, true);
17764
+ function visit(node2, scope, isProgram = false) {
17765
+ let childScope = scope;
17766
+ let skip = NOTHING;
17767
+ switch (node2.type) {
17768
+ case "ImportDeclaration":
17769
+ case "ExportAllDeclaration":
17770
+ return;
17771
+ case "ExportNamedDeclaration":
17772
+ if (!node2.declaration) return;
17773
+ break;
17774
+ case "FunctionDeclaration":
17775
+ case "FunctionExpression":
17776
+ case "ArrowFunctionExpression": {
17777
+ const names = /* @__PURE__ */ new Set();
17778
+ for (const param of node2.params ?? []) collectPattern(param, names);
17779
+ const id = node2.id;
17780
+ if (id && node2.type === "FunctionExpression") names.add(id.name);
17781
+ const fnBody = node2.body;
17782
+ if (fnBody?.type === "BlockStatement") {
17783
+ for (const name of hoistedNames(fnBody.body)) names.add(name);
17784
+ }
17785
+ childScope = { names, parent: scope };
17786
+ break;
17787
+ }
17788
+ case "CatchClause": {
17789
+ const names = /* @__PURE__ */ new Set();
17790
+ if (node2.param) collectPattern(node2.param, names);
17791
+ childScope = { names, parent: scope };
17792
+ break;
17793
+ }
17794
+ case "ClassExpression": {
17795
+ const id = node2.id;
17796
+ if (id) childScope = { names: /* @__PURE__ */ new Set([id.name]), parent: scope };
17797
+ break;
17798
+ }
17799
+ case "BlockStatement":
17800
+ case "StaticBlock":
17801
+ if (!isProgram) {
17802
+ childScope = { names: blockNames(node2.body), parent: scope };
17803
+ }
17804
+ break;
17805
+ case "ForStatement":
17806
+ case "ForInStatement":
17807
+ case "ForOfStatement": {
17808
+ const head2 = node2.init ?? node2.left;
17809
+ if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
17810
+ const names = /* @__PURE__ */ new Set();
17811
+ for (const declarator of head2.declarations) {
17812
+ collectPattern(declarator.id, names);
17813
+ }
17814
+ childScope = { names, parent: scope };
17815
+ }
17816
+ break;
17817
+ }
17818
+ case "MetaProperty":
17819
+ edits.push({ start: node2.start, end: node2.end, text: HELPERS.meta });
17820
+ return;
17821
+ case "ImportExpression": {
17822
+ const argument = node2.source;
17823
+ edits.push({ start: node2.start, end: argument.start, text: `${HELPERS.dynamic}(` });
17824
+ visit(argument, childScope);
17825
+ edits.push({ start: argument.end, end: node2.end, text: ")" });
17826
+ return;
17827
+ }
17828
+ case "Identifier": {
17829
+ const replacement = lookup(node2.name, scope);
17830
+ if (replacement) edits.push({ start: node2.start, end: node2.end, text: replacement });
17831
+ return;
17832
+ }
17833
+ case "MemberExpression":
17834
+ case "MethodDefinition":
17835
+ case "PropertyDefinition":
17836
+ skip = node2.computed ? NOTHING : PROPERTY;
17837
+ break;
17838
+ case "Property": {
17839
+ if (node2.computed) break;
17840
+ if (node2.shorthand) {
17841
+ const value = node2.value;
17842
+ if (value.type === "Identifier") {
17843
+ const replacement = lookup(value.name, scope);
17844
+ if (replacement) {
17845
+ edits.push({ start: value.start, end: value.end, text: `${value.name}: ${replacement}` });
17846
+ return;
17847
+ }
17848
+ }
17849
+ break;
17850
+ }
17851
+ skip = PROPERTY;
17852
+ break;
17853
+ }
17854
+ case "LabeledStatement":
17855
+ case "BreakStatement":
17856
+ case "ContinueStatement":
17857
+ skip = LABEL;
17858
+ break;
17859
+ }
17860
+ for (const [key, value] of Object.entries(node2)) {
17861
+ if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
17862
+ if (Array.isArray(value)) {
17863
+ for (const item of value) if (isNode2(item)) visit(item, childScope);
17864
+ } else if (isNode2(value)) {
17865
+ visit(value, childScope);
17866
+ }
17867
+ }
17828
17868
  }
17829
- try {
17830
- ctx.vfs.appendFile(historyPath, trimmed + "\n", { cred: ctx.cred });
17831
- } catch {
17869
+ function lookup(name, scope) {
17870
+ for (let current = scope; current; current = current.parent) {
17871
+ if (current.names.has(name)) {
17872
+ return current.parent === null ? importBindings.get(name) ?? null : null;
17873
+ }
17874
+ }
17875
+ return null;
17832
17876
  }
17833
17877
  }
17834
- return 0;
17835
17878
  }
17836
- async function runQuiet(ctx, script) {
17837
- try {
17838
- const proc = await ctx.kernel.pod.spawn("node", [script], { cwd: ctx.cwd, env: ctx.env });
17839
- return await proc.completion;
17840
- } catch (e) {
17841
- return { exitCode: 1, stdout: "", stderr: `${e instanceof Error ? e.message : String(e)}
17842
- ` };
17879
+ var JSX_EXTENSION = /\.[jt]sx$/;
17880
+ var JsxParser = Parser$1.extend(jsx());
17881
+ var PARSE_OPTIONS = {
17882
+ ecmaVersion: "latest",
17883
+ sourceType: "module",
17884
+ allowAwaitOutsideFunction: true,
17885
+ allowHashBang: true,
17886
+ allowReturnOutsideFunction: true
17887
+ };
17888
+ function parseModule(source, filename) {
17889
+ const parser = JSX_EXTENSION.test(filename) ? JsxParser.parse.bind(JsxParser) : parse$1;
17890
+ return parser(source, PARSE_OPTIONS);
17891
+ }
17892
+ function containsImportExpression(node2) {
17893
+ if (Array.isArray(node2)) return node2.some(containsImportExpression);
17894
+ if (!isNode2(node2)) return false;
17895
+ if (node2.type === "ImportExpression") return true;
17896
+ for (const [key, value] of Object.entries(node2)) {
17897
+ if (key === "type" || key === "start" || key === "end") continue;
17898
+ if (containsImportExpression(value)) return true;
17843
17899
  }
17900
+ return false;
17844
17901
  }
17845
- function printHelp(ctx) {
17846
- ctx.line("Usage: node [options] [script.js] [arguments]");
17847
- ctx.line("");
17848
- ctx.line("Options:");
17849
- ctx.line(" -v, --version print Node.js version");
17850
- ctx.line(" -e, --eval script evaluate script");
17851
- ctx.line(" -p, --print evaluate script and print result");
17852
- ctx.line(" -h, --help print this message");
17902
+ function containsImportMeta(node2) {
17903
+ if (Array.isArray(node2)) return node2.some(containsImportMeta);
17904
+ if (!isNode2(node2)) return false;
17905
+ if (node2.type === "MetaProperty") return true;
17906
+ for (const [key, value] of Object.entries(node2)) {
17907
+ if (key === "type" || key === "start" || key === "end") continue;
17908
+ if (containsImportMeta(value)) return true;
17909
+ }
17910
+ return false;
17853
17911
  }
17854
- var nodeVersionFile = defineCommand({
17855
- name: "nvm",
17856
- path: "/usr/local/bin/nvm",
17857
- summary: "node version manager (single-version stub)",
17858
- run(ctx) {
17859
- const sub = ctx.args[0];
17860
- if (sub === "current" || sub === "version") {
17861
- ctx.line(NODE_VERSION);
17862
- return 0;
17912
+ function hasTopLevelAwait(node2) {
17913
+ if (Array.isArray(node2)) return node2.some(hasTopLevelAwait);
17914
+ if (!isNode2(node2)) return false;
17915
+ if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
17916
+ return false;
17917
+ }
17918
+ if (node2.type === "AwaitExpression") return true;
17919
+ if (node2.type === "ForOfStatement" && node2.await === true) return true;
17920
+ for (const [key, value] of Object.entries(node2)) {
17921
+ if (key === "type" || key === "start" || key === "end") continue;
17922
+ if (hasTopLevelAwait(value)) return true;
17923
+ }
17924
+ return false;
17925
+ }
17926
+ function declaredNames(declaration) {
17927
+ const names = /* @__PURE__ */ new Set();
17928
+ if (declaration.type === "VariableDeclaration") {
17929
+ for (const declarator of declaration.declarations) {
17930
+ collectPattern(declarator.id, names);
17863
17931
  }
17864
- if (sub === "ls" || sub === "list") {
17865
- ctx.line(`-> ${NODE_VERSION}`);
17866
- return 0;
17932
+ } else if (isNode2(declaration.id)) {
17933
+ names.add(declaration.id.name);
17934
+ }
17935
+ return [...names];
17936
+ }
17937
+ function nameOf(node2) {
17938
+ return node2.type === "Identifier" ? node2.name : node2.value;
17939
+ }
17940
+ function hoistedNames(body) {
17941
+ const names = blockNames(body);
17942
+ collectVars(body, names);
17943
+ return names;
17944
+ }
17945
+ function blockNames(body) {
17946
+ const names = /* @__PURE__ */ new Set();
17947
+ for (const node2 of body ?? []) {
17948
+ if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
17949
+ for (const declarator of node2.declarations) {
17950
+ collectPattern(declarator.id, names);
17951
+ }
17952
+ } else if ((node2.type === "ClassDeclaration" || node2.type === "FunctionDeclaration") && isNode2(node2.id)) {
17953
+ names.add(node2.id.name);
17867
17954
  }
17868
- ctx.line(`nvm: only ${NODE_VERSION} is available in this container`);
17869
- return 0;
17870
17955
  }
17871
- });
17872
- function nodeCommands() {
17873
- return [node, nodeVersionFile];
17956
+ return names;
17874
17957
  }
17875
-
17876
- // src/python/python.ts
17877
- init_path();
17878
-
17879
- // src/python/host-abi.ts
17880
- var SBX_HOST_ABI_VERSION = 1;
17881
- var SBX_REQUEST_HEADER_BYTES = 16;
17882
- var SBX_RESPONSE_HEADER_BYTES = 20;
17883
- var Op = {
17884
- handshake: 0,
17885
- openat: 1,
17886
- close: 2,
17887
- read: 3,
17888
- write: 4,
17889
- pread: 5,
17890
- pwrite: 6,
17891
- lseek: 7,
17892
- fstat: 8,
17893
- statat: 9,
17894
- ftruncate: 10,
17895
- renameat: 11,
17896
- unlinkat: 12,
17897
- mkdirat: 13,
17898
- readlinkat: 14,
17899
- symlinkat: 15,
17900
- getdents: 16,
17901
- fsync: 17,
17902
- dup: 256,
17903
- dup2: 257,
17904
- get_flags: 258,
17905
- set_flags: 259,
17906
- pipe: 512,
17907
- poll: 768,
17908
- clock_gettime: 1024,
17909
- sleep: 1025,
17910
- getpid: 1280,
17911
- getcwd: 1281,
17912
- chdir: 1282,
17913
- environ: 1283,
17958
+ function collectVars(nodes, names) {
17959
+ if (Array.isArray(nodes)) {
17960
+ for (const item of nodes) collectVars(item, names);
17961
+ return;
17962
+ }
17963
+ if (!isNode2(nodes)) return;
17964
+ const node2 = nodes;
17965
+ if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
17966
+ if (isNode2(node2.id)) names.add(node2.id.name);
17967
+ return;
17968
+ }
17969
+ if (node2.type === "VariableDeclaration" && node2.kind === "var") {
17970
+ for (const declarator of node2.declarations) {
17971
+ collectPattern(declarator.id, names);
17972
+ }
17973
+ }
17974
+ for (const [key, value] of Object.entries(node2)) {
17975
+ if (key === "type" || key === "start" || key === "end") continue;
17976
+ collectVars(value, names);
17977
+ }
17978
+ }
17979
+ function collectPattern(node2, names) {
17980
+ if (!isNode2(node2)) return;
17981
+ switch (node2.type) {
17982
+ case "Identifier":
17983
+ names.add(node2.name);
17984
+ return;
17985
+ case "ObjectPattern":
17986
+ for (const property of node2.properties) {
17987
+ collectPattern(property.value ?? property.argument, names);
17988
+ }
17989
+ return;
17990
+ case "ArrayPattern":
17991
+ for (const element of node2.elements) collectPattern(element, names);
17992
+ return;
17993
+ case "AssignmentPattern":
17994
+ collectPattern(node2.left, names);
17995
+ return;
17996
+ case "RestElement":
17997
+ collectPattern(node2.argument, names);
17998
+ return;
17999
+ default:
18000
+ return;
18001
+ }
18002
+ }
18003
+ function applyEdits(source, edits) {
18004
+ const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
18005
+ let out = source;
18006
+ for (const edit of ordered) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
18007
+ return out;
18008
+ }
18009
+ function isNode2(value) {
18010
+ return typeof value === "object" && value !== null && typeof value.type === "string";
18011
+ }
18012
+ var NOTHING = [];
18013
+ var PROPERTY = ["property", "key"];
18014
+ var LABEL = ["label"];
18015
+
18016
+ // src/node/node.ts
18017
+ var NODE_VERSION = "v22.12.0";
18018
+ new TextEncoder();
18019
+ var tempCounter = 0;
18020
+ function tempPath(kind, pid, ext = ".js") {
18021
+ return `/tmp/.sbx-${kind}-${pid}-${tempCounter++}${ext}`;
18022
+ }
18023
+ function withPrelude(source, prelude) {
18024
+ if (!source.startsWith("#!")) return prelude + source;
18025
+ const eol = source.indexOf("\n");
18026
+ if (eol < 0) return source + "\n" + prelude;
18027
+ return source.slice(0, eol + 1) + prelude + source.slice(eol + 1);
18028
+ }
18029
+ function siblingPath(script, pid) {
18030
+ const ext = extname(script) || ".js";
18031
+ return join(dirname(script), `.sbx-${pid}-${tempCounter++}-${basename(script, ext)}${ext}`);
18032
+ }
18033
+ var BUFFER_COMPAT_SHIM = `
18034
+ (function () {
18035
+ const seen = new Set();
18036
+ const widen = (B) => {
18037
+ if (!B || seen.has(B) || typeof B.isBuffer !== 'function') return;
18038
+ seen.add(B);
18039
+ const original = B.isBuffer.bind(B);
18040
+ B.isBuffer = (value) => original(value) || value instanceof Uint8Array;
18041
+ };
18042
+ try { widen(globalThis.Buffer); } catch (e) {}
18043
+ try { widen(require('buffer').Buffer); } catch (e) {}
18044
+ })();
18045
+ `;
18046
+ function buildPrelude(scriptPath, argv, stdinPath, preloads = []) {
18047
+ const argvLiteral = JSON.stringify(["/usr/bin/node", scriptPath, ...argv]);
18048
+ const pathLiteral = JSON.stringify(scriptPath);
18049
+ const stdinLiteral = JSON.stringify(stdinPath);
18050
+ return `${BUFFER_COMPAT_SHIM}
18051
+ process.argv = ${argvLiteral};
18052
+ try {
18053
+ __filename = ${pathLiteral};
18054
+ module.filename = ${pathLiteral};
18055
+ module.id = ${pathLiteral};
18056
+ if (require.main === module) require.main.filename = ${pathLiteral};
18057
+ } catch (e) {}
18058
+ ${stdinPath === null ? "" : `(function () {
18059
+ const fs = require('fs');
18060
+ const { Readable } = require('stream');
18061
+ let data;
18062
+ try { data = fs.readFileSync(${stdinLiteral}); } catch (e) { data = Buffer.alloc(0); }
18063
+ const stream = Readable.from([data]);
18064
+ stream.isTTY = false;
18065
+ stream.fd = 0;
18066
+ stream.setRawMode = function () { return this; };
18067
+ try {
18068
+ Object.defineProperty(process, 'stdin', { value: stream, configurable: true, writable: true });
18069
+ } catch (e) { process.stdin = stream; }
18070
+ })();
18071
+ `}${preloadRequires(preloads)}`;
18072
+ }
18073
+ function preloadRequires(preloads) {
18074
+ return preloads.map((specifier) => `require(${JSON.stringify(specifier)});
18075
+ `).join("");
18076
+ }
18077
+ async function execute(ctx, invocation) {
18078
+ const { pod } = ctx.kernel;
18079
+ const live = ctx.stdin.interactive || ctx.stdin.isTTY;
18080
+ const tty = ctx.stdin.isTTY;
18081
+ let exitCode = 0;
18082
+ try {
18083
+ const proc = await pod.spawn("node", [invocation.script], {
18084
+ cwd: ctx.cwd,
18085
+ env: { ...invocation.env ?? ctx.env, PWD: ctx.cwd },
18086
+ argv: ["/usr/bin/node", invocation.argvPath ?? invocation.script, ...invocation.argv],
18087
+ ...invocation.stdinPath ? { stdinPath: invocation.stdinPath } : {},
18088
+ /* An interactive caller's input was not captured to a file, so the
18089
+ * program's stdin has to stay open and be fed as it arrives — and be
18090
+ * reported as a terminal, since that is what decides whether a CLI
18091
+ * prompts or takes its defaults. */
18092
+ ...live ? { interactiveStdin: true } : {},
18093
+ ...tty ? { tty: true } : {}
18094
+ });
18095
+ proc.on("output", (chunk) => {
18096
+ try {
18097
+ ctx.write(chunk);
18098
+ } catch {
18099
+ }
18100
+ });
18101
+ proc.on("error", (chunk) => {
18102
+ try {
18103
+ ctx.stderr.write(chunk);
18104
+ } catch {
18105
+ }
18106
+ });
18107
+ proc.on("rawmode", (enabled) => {
18108
+ ctx.stdin.rawMode = enabled;
18109
+ });
18110
+ let forwarding = false;
18111
+ if (live) {
18112
+ forwarding = true;
18113
+ void (async () => {
18114
+ while (forwarding) {
18115
+ const chunk = await ctx.stdin.read();
18116
+ if (chunk === null) {
18117
+ proc.endInput?.();
18118
+ break;
18119
+ }
18120
+ try {
18121
+ proc.write(new TextDecoder().decode(chunk));
18122
+ } catch {
18123
+ break;
18124
+ }
18125
+ }
18126
+ })();
18127
+ }
18128
+ let onAbort;
18129
+ const aborted = new Promise((resolve3) => {
18130
+ onAbort = () => {
18131
+ try {
18132
+ proc.kill();
18133
+ } catch {
18134
+ }
18135
+ resolve3({ exitCode: 137 });
18136
+ };
18137
+ if (ctx.signal.aborted) onAbort();
18138
+ else ctx.signal.addEventListener("abort", onAbort, { once: true });
18139
+ });
18140
+ const result = await Promise.race([proc.completion, aborted]);
18141
+ forwarding = false;
18142
+ if (onAbort) ctx.signal.removeEventListener("abort", onAbort);
18143
+ exitCode = result.exitCode;
18144
+ } catch (e) {
18145
+ ctx.stderr.write(`node: ${e instanceof Error ? e.message : String(e)}
18146
+ `);
18147
+ exitCode = 1;
18148
+ } finally {
18149
+ for (const temp of invocation.temps) {
18150
+ try {
18151
+ ctx.vfs.unlink(temp);
18152
+ } catch {
18153
+ }
18154
+ }
18155
+ }
18156
+ return exitCode;
18157
+ }
18158
+ async function captureStdin(ctx) {
18159
+ if (ctx.stdin.isTTY || ctx.stdin.interactive) return null;
18160
+ const bytes2 = await ctx.stdin.readAll();
18161
+ if (bytes2.length === 0) return null;
18162
+ const path = tempPath("stdin", ctx.proc.pid, ".dat");
18163
+ ctx.vfs.writeFile(path, bytes2, { privileged: true, mode: 384 });
18164
+ return path;
18165
+ }
18166
+ var node = defineCommand({
18167
+ name: "node",
18168
+ path: "/usr/bin/node",
18169
+ aliases: ["nodejs"],
18170
+ summary: "run a JavaScript program with Node.js",
18171
+ usage: "node [options] [script.js] [arguments]",
18172
+ manual: `Runs JavaScript with a real Node.js API surface \u2014 fs, path, http,
18173
+ crypto, streams, worker_threads and npm packages \u2014 inside the container.
18174
+ The filesystem a script sees is the container's filesystem.`,
18175
+ async run(ctx) {
18176
+ const argv = ctx.args;
18177
+ const temps = [];
18178
+ let i = 0;
18179
+ let evalSource;
18180
+ let printResult = false;
18181
+ let scriptArg;
18182
+ const preloads = [];
18183
+ const envFiles = [];
18184
+ let testMode = false;
18185
+ let checkOnly = false;
18186
+ const testSettings = { namePatterns: [] };
18187
+ for (; i < argv.length; i++) {
18188
+ const arg = argv[i];
18189
+ if (arg === "-v" || arg === "--version") {
18190
+ ctx.line(NODE_VERSION);
18191
+ return 0;
18192
+ }
18193
+ if (arg === "-h" || arg === "--help") {
18194
+ printHelp(ctx);
18195
+ return 0;
18196
+ }
18197
+ if (arg === "-e" || arg === "--eval") {
18198
+ evalSource = argv[++i] ?? "";
18199
+ i++;
18200
+ break;
18201
+ }
18202
+ if (arg === "-p" || arg === "--print") {
18203
+ printResult = true;
18204
+ evalSource = argv[++i] ?? "";
18205
+ i++;
18206
+ break;
18207
+ }
18208
+ if (arg.startsWith("--eval=")) {
18209
+ evalSource = arg.slice("--eval=".length);
18210
+ i++;
18211
+ break;
18212
+ }
18213
+ if (arg.startsWith("--print=")) {
18214
+ printResult = true;
18215
+ evalSource = arg.slice("--print=".length);
18216
+ i++;
18217
+ break;
18218
+ }
18219
+ if (arg === "--") {
18220
+ i++;
18221
+ break;
18222
+ }
18223
+ const flag = arg.startsWith("--") ? arg.split("=", 1)[0] : arg;
18224
+ if (VALUE_FLAGS.has(flag)) {
18225
+ const value = arg.length > flag.length ? arg.slice(flag.length + 1) : argv[++i];
18226
+ if (value === void 0) {
18227
+ ctx.stderr.write(`node: ${flag} requires an argument
18228
+ `);
18229
+ return 9;
18230
+ }
18231
+ if (flag === "-r" || flag === "--require" || flag === "--import") preloads.push(value);
18232
+ else if (flag === "--env-file") envFiles.push({ path: value, optional: false });
18233
+ else if (flag === "--env-file-if-exists") envFiles.push({ path: value, optional: true });
18234
+ else if (flag === "--test-reporter") testSettings.reporter = value === "tap" ? "tap" : "spec";
18235
+ else if (flag === "--test-name-pattern") testSettings.namePatterns.push(value);
18236
+ else if (flag === "--loader" || flag === "--experimental-loader") {
18237
+ ctx.stderr.write(`node: ${flag} is not supported in this container; ${value} was not loaded
18238
+ `);
18239
+ }
18240
+ continue;
18241
+ }
18242
+ if (arg === "--test") {
18243
+ testMode = true;
18244
+ continue;
18245
+ }
18246
+ if (arg === "--test-only") {
18247
+ testSettings.only = true;
18248
+ continue;
18249
+ }
18250
+ if (arg === "-c" || arg === "--check") {
18251
+ checkOnly = true;
18252
+ continue;
18253
+ }
18254
+ if (flag === "--watch" || flag === "--watch-path" || flag === "--watch-preserve-output") {
18255
+ ctx.stderr.write(
18256
+ `node: ${flag} is not available in this container: file changes are not watched. Run the program again after changing it.
18257
+ `
18258
+ );
18259
+ return 9;
18260
+ }
18261
+ if (arg.startsWith("-") && arg !== "-") {
18262
+ continue;
18263
+ }
18264
+ scriptArg = arg;
18265
+ i++;
18266
+ break;
18267
+ }
18268
+ const scriptArgs = argv.slice(i);
18269
+ let env2;
18270
+ if (envFiles.length > 0) {
18271
+ const loaded = {};
18272
+ for (const file3 of envFiles) {
18273
+ let text2;
18274
+ try {
18275
+ text2 = ctx.vfs.readText(ctx.path(file3.path), ctx.cred);
18276
+ } catch {
18277
+ if (file3.optional) continue;
18278
+ ctx.stderr.write(`node: ${file3.path}: not found
18279
+ `);
18280
+ return 9;
18281
+ }
18282
+ Object.assign(loaded, parseEnvFile(text2));
18283
+ }
18284
+ env2 = { ...loaded, ...ctx.env };
18285
+ }
18286
+ const preloadSpecs = preloads.map((spec) => spec.startsWith(".") || spec.startsWith("/") ? ctx.path(spec) : spec);
18287
+ if (testMode) {
18288
+ return runTestFiles(ctx, scriptArg === void 0 ? scriptArgs : [scriptArg, ...scriptArgs], testSettings, preloads, env2 ?? ctx.env);
18289
+ }
18290
+ if (checkOnly && scriptArg !== void 0) {
18291
+ const script2 = resolveScript(ctx, scriptArg);
18292
+ if (!script2) {
18293
+ ctx.stderr.write(`node: cannot find module '${ctx.path(scriptArg)}'
18294
+ `);
18295
+ return 1;
18296
+ }
18297
+ return checkSyntax(ctx, script2);
18298
+ }
18299
+ if (evalSource !== void 0) {
18300
+ const body = printResult ? `const __sbxResult = (function(){ return (${evalSource}
18301
+ ); })();
18302
+ console.log(typeof __sbxResult === 'string' ? __sbxResult : require('util').inspect(__sbxResult));` : evalSource;
18303
+ const path = tempPath("eval", ctx.proc.pid);
18304
+ const stdinPath2 = await captureStdin(ctx);
18305
+ if (stdinPath2) temps.push(stdinPath2);
18306
+ ctx.vfs.writeFile(path, buildBootstrapForEval(body, ["/usr/bin/node", ...scriptArgs], stdinPath2, preloadSpecs), {
18307
+ privileged: true,
18308
+ mode: 384
18309
+ });
18310
+ temps.push(path);
18311
+ return execute(ctx, { script: path, argv: scriptArgs, stdinPath: stdinPath2, temps, ...env2 ? { env: env2 } : {} });
18312
+ }
18313
+ if (scriptArg === void 0 || scriptArg === "-") {
18314
+ if (ctx.stdin.isTTY) {
18315
+ ctx.line(`Welcome to Node.js ${NODE_VERSION}.`);
18316
+ ctx.line('Type ".help" for more information.');
18317
+ return runRepl(ctx);
18318
+ }
18319
+ const source = new TextDecoder().decode(await ctx.stdin.readAll());
18320
+ if (source.trim() === "") return 0;
18321
+ const path = tempPath("stdin-script", ctx.proc.pid);
18322
+ ctx.vfs.writeFile(path, buildBootstrapForEval(source, ["/usr/bin/node", ...scriptArgs], null, preloadSpecs), {
18323
+ privileged: true,
18324
+ mode: 384
18325
+ });
18326
+ temps.push(path);
18327
+ return execute(ctx, { script: path, argv: scriptArgs, temps, ...env2 ? { env: env2 } : {} });
18328
+ }
18329
+ const script = resolveScript(ctx, scriptArg);
18330
+ if (!script) {
18331
+ ctx.stderr.write(
18332
+ `node:internal/modules/cjs/loader:1215
18333
+ throw err;
18334
+ ^
18335
+
18336
+ Error: Cannot find module '${ctx.path(scriptArg)}'
18337
+ `
18338
+ );
18339
+ return 1;
18340
+ }
18341
+ const stdinPath = await captureStdin(ctx);
18342
+ if (stdinPath) temps.push(stdinPath);
18343
+ const sibling = siblingPath(script, ctx.proc.pid);
18344
+ try {
18345
+ const source = ctx.vfs.readText(script, ctx.cred);
18346
+ ctx.vfs.writeFile(sibling, withPrelude(source, buildPrelude(script, scriptArgs, stdinPath, preloadSpecs)), {
18347
+ privileged: true,
18348
+ mode: 384
18349
+ });
18350
+ temps.push(sibling);
18351
+ return await execute(ctx, {
18352
+ script: sibling,
18353
+ argvPath: script,
18354
+ argv: scriptArgs,
18355
+ stdinPath,
18356
+ temps,
18357
+ ...env2 ? { env: env2 } : {}
18358
+ });
18359
+ } catch {
18360
+ return await execute(ctx, { script, argv: scriptArgs, stdinPath, temps, ...env2 ? { env: env2 } : {} });
18361
+ }
18362
+ }
18363
+ });
18364
+ function buildBootstrapForEval(source, argv, stdinPath, preloads = []) {
18365
+ const prelude = stdinPath ? `const fs = require('fs');
18366
+ const { Readable } = require('stream');
18367
+ let __sbxData; try { __sbxData = fs.readFileSync(${JSON.stringify(stdinPath)}); } catch (e) { __sbxData = Buffer.alloc(0); }
18368
+ const __sbxStdin = Readable.from([__sbxData]);
18369
+ __sbxStdin.isTTY = false;
18370
+ __sbxStdin.setRawMode = function () { return this; };
18371
+ try { Object.defineProperty(process, 'stdin', { value: __sbxStdin, configurable: true, writable: true }); } catch (e) { process.stdin = __sbxStdin; }
18372
+ ` : "";
18373
+ return `${BUFFER_COMPAT_SHIM}process.argv = ${JSON.stringify(argv)};
18374
+ ${prelude}${preloadRequires(preloads)}${source}
18375
+ `;
18376
+ }
18377
+ function resolveScript(ctx, operand) {
18378
+ const base2 = ctx.path(operand);
18379
+ const candidates = [base2, `${base2}.js`, `${base2}.mjs`, `${base2}.cjs`, `${base2}.json`];
18380
+ for (const candidate of candidates) {
18381
+ try {
18382
+ const st = ctx.vfs.stat(candidate, { cred: ctx.cred });
18383
+ if (st.isFile()) return ctx.vfs.realpath(candidate, ctx.cred);
18384
+ if (st.isDirectory()) {
18385
+ const manifest = join(candidate, "package.json");
18386
+ if (ctx.vfs.lexists(manifest)) {
18387
+ try {
18388
+ const main = JSON.parse(ctx.vfs.readText(manifest, ctx.cred)).main;
18389
+ if (main) {
18390
+ const mainPath = resolve(candidate, main);
18391
+ if (ctx.vfs.lexists(mainPath)) return ctx.vfs.realpath(mainPath, ctx.cred);
18392
+ if (ctx.vfs.lexists(mainPath + ".js")) return ctx.vfs.realpath(mainPath + ".js", ctx.cred);
18393
+ }
18394
+ } catch {
18395
+ }
18396
+ }
18397
+ for (const index of ["index.js", "index.mjs", "index.cjs"]) {
18398
+ const path = join(candidate, index);
18399
+ if (ctx.vfs.lexists(path)) return ctx.vfs.realpath(path, ctx.cred);
18400
+ }
18401
+ }
18402
+ } catch {
18403
+ }
18404
+ }
18405
+ return null;
18406
+ }
18407
+ async function runRepl(ctx) {
18408
+ const historyPath = join(ctx.env.HOME ?? "/root", ".node_repl_history");
18409
+ const statements = [];
18410
+ for (; ; ) {
18411
+ ctx.write("> ");
18412
+ const line = await ctx.stdin.readLine();
18413
+ if (line === null) {
18414
+ ctx.line("");
18415
+ break;
18416
+ }
18417
+ const trimmed = line.trim();
18418
+ if (trimmed === ".exit") break;
18419
+ if (trimmed === ".help") {
18420
+ ctx.line(".exit Exit the REPL");
18421
+ ctx.line(".help Print this help message");
18422
+ ctx.line(".clear Reset the session context");
18423
+ continue;
18424
+ }
18425
+ if (trimmed === ".clear") {
18426
+ statements.length = 0;
18427
+ continue;
18428
+ }
18429
+ if (trimmed === "") continue;
18430
+ const program = [...statements, `console.log(require('util').inspect((${trimmed})))`].join(";\n");
18431
+ const fallback = [...statements, trimmed].join(";\n");
18432
+ const path = tempPath("repl", ctx.proc.pid);
18433
+ ctx.vfs.writeFile(path, program, { privileged: true, mode: 384 });
18434
+ const result = await runQuiet(ctx, path);
18435
+ if (result.exitCode !== 0) {
18436
+ ctx.vfs.writeFile(path, fallback, { privileged: true, mode: 384 });
18437
+ const second = await runQuiet(ctx, path);
18438
+ ctx.write(second.stdout);
18439
+ if (second.exitCode === 0) statements.push(trimmed);
18440
+ else ctx.stderr.write(second.stderr);
18441
+ } else {
18442
+ ctx.write(result.stdout);
18443
+ statements.push(trimmed);
18444
+ }
18445
+ try {
18446
+ ctx.vfs.unlink(path);
18447
+ } catch {
18448
+ }
18449
+ try {
18450
+ ctx.vfs.appendFile(historyPath, trimmed + "\n", { cred: ctx.cred });
18451
+ } catch {
18452
+ }
18453
+ }
18454
+ return 0;
18455
+ }
18456
+ async function runQuiet(ctx, script) {
18457
+ try {
18458
+ const proc = await ctx.kernel.pod.spawn("node", [script], { cwd: ctx.cwd, env: ctx.env });
18459
+ return await proc.completion;
18460
+ } catch (e) {
18461
+ return { exitCode: 1, stdout: "", stderr: `${e instanceof Error ? e.message : String(e)}
18462
+ ` };
18463
+ }
18464
+ }
18465
+ function printHelp(ctx) {
18466
+ ctx.line("Usage: node [options] [script.js] [arguments]");
18467
+ ctx.line(" node --test [files or globs]");
18468
+ ctx.line("");
18469
+ ctx.line("Options:");
18470
+ ctx.line(" -v, --version print Node.js version");
18471
+ ctx.line(" -e, --eval script evaluate script");
18472
+ ctx.line(" -p, --print evaluate script and print result");
18473
+ ctx.line(" -c, --check check syntax without running");
18474
+ ctx.line(" -r, --require module load a module before the program");
18475
+ ctx.line(" --import module load a module before the program");
18476
+ ctx.line(" --env-file file load environment variables from a file");
18477
+ ctx.line(" --test run test files with node:test");
18478
+ ctx.line(" --test-reporter spec|tap choose the test report format");
18479
+ ctx.line(" --test-name-pattern regex run only tests whose name matches");
18480
+ ctx.line(" -h, --help print this message");
18481
+ }
18482
+ var VALUE_FLAGS = /* @__PURE__ */ new Set([
18483
+ "-r",
18484
+ "--require",
18485
+ "--import",
18486
+ "--loader",
18487
+ "--experimental-loader",
18488
+ "--env-file",
18489
+ "--env-file-if-exists",
18490
+ "--input-type",
18491
+ "--title",
18492
+ "-C",
18493
+ "--conditions",
18494
+ "--test-reporter",
18495
+ "--test-reporter-destination",
18496
+ "--test-name-pattern",
18497
+ "--test-skip-pattern",
18498
+ "--test-concurrency",
18499
+ "--test-timeout",
18500
+ "--test-shard",
18501
+ "--disable-warning",
18502
+ "--redirect-warnings",
18503
+ "--stack-trace-limit",
18504
+ "--inspect-port",
18505
+ "--report-dir",
18506
+ "--report-filename"
18507
+ ]);
18508
+ function parseEnvFile(text2) {
18509
+ const out = {};
18510
+ for (const raw of text2.split(/\r?\n/)) {
18511
+ const line = raw.trim();
18512
+ if (!line || line.startsWith("#")) continue;
18513
+ const match2 = /^(?:export\s+)?([A-Za-z_][A-Za-z0-9_.-]*)\s*=\s*(.*)$/.exec(line);
18514
+ if (!match2) continue;
18515
+ let value = match2[2];
18516
+ const quote3 = value[0];
18517
+ if ((quote3 === '"' || quote3 === "'" || quote3 === "`") && value.length >= 2 && value.endsWith(quote3)) {
18518
+ value = value.slice(1, -1);
18519
+ if (quote3 === '"') value = value.replace(/\\n/g, "\n");
18520
+ } else {
18521
+ const comment = value.indexOf(" #");
18522
+ if (comment >= 0) value = value.slice(0, comment);
18523
+ value = value.trim();
18524
+ }
18525
+ out[match2[1]] = value;
18526
+ }
18527
+ return out;
18528
+ }
18529
+ var AsyncFunction = Object.getPrototypeOf(async function() {
18530
+ }).constructor;
18531
+ function checkSyntax(ctx, script) {
18532
+ let source = ctx.vfs.readText(script, ctx.cred);
18533
+ if (source.startsWith("#!")) source = `//${source.slice(2)}`;
18534
+ try {
18535
+ const esm = script.endsWith(".mjs") || !script.endsWith(".cjs") && looksLikeEsm(source);
18536
+ const transformed = esm ? transformEsm(source, script) : null;
18537
+ new AsyncFunction("exports", "require", "module", "__filename", "__dirname", transformed?.code ?? source);
18538
+ return 0;
18539
+ } catch (error) {
18540
+ if (!(error instanceof SyntaxError)) throw error;
18541
+ ctx.stderr.write(`${script}
18542
+
18543
+ ${error.name}: ${error.message}
18544
+
18545
+ Node.js ${NODE_VERSION}
18546
+ `);
18547
+ return 1;
18548
+ }
18549
+ }
18550
+ var TEST_FILE_NAME = /^(?:test|test-.+|.+[.\-_]test)\.(?:c|m)?js$/;
18551
+ var SCRIPT_FILE = /\.(?:c|m)?js$/;
18552
+ function globToRegExp(pattern) {
18553
+ let out = "";
18554
+ for (let index = 0; index < pattern.length; index += 1) {
18555
+ const ch = pattern[index];
18556
+ if (ch === "*") {
18557
+ if (pattern[index + 1] === "*") {
18558
+ index += 1;
18559
+ if (pattern[index + 1] === "/") index += 1;
18560
+ out += "(?:.*/)?";
18561
+ } else out += "[^/]*";
18562
+ } else if (ch === "?") out += "[^/]";
18563
+ else out += ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
18564
+ }
18565
+ return new RegExp(`^${out}$`);
18566
+ }
18567
+ function discoverTestFiles(ctx, patterns) {
18568
+ const files = /* @__PURE__ */ new Set();
18569
+ const missing = [];
18570
+ const walk = (dir3, visit, underTestDir = false) => {
18571
+ let names;
18572
+ try {
18573
+ names = ctx.vfs.readdir(dir3, ctx.cred);
18574
+ } catch {
18575
+ return;
18576
+ }
18577
+ for (const name of [...names].sort()) {
18578
+ if (name === "node_modules" || name.startsWith(".")) continue;
18579
+ const path = join(dir3, name);
18580
+ let isDirectory;
18581
+ try {
18582
+ isDirectory = ctx.vfs.stat(path, { cred: ctx.cred }).isDirectory();
18583
+ } catch {
18584
+ continue;
18585
+ }
18586
+ if (isDirectory) walk(path, visit, underTestDir || name === "test");
18587
+ else visit(path, name, underTestDir);
18588
+ }
18589
+ };
18590
+ const byName = (path, name, underTestDir) => {
18591
+ if (SCRIPT_FILE.test(name) && (underTestDir || TEST_FILE_NAME.test(name))) files.add(path);
18592
+ };
18593
+ if (patterns.length === 0) walk(ctx.cwd, byName);
18594
+ for (const pattern of patterns) {
18595
+ const target = ctx.path(pattern);
18596
+ let stat2 = null;
18597
+ try {
18598
+ stat2 = ctx.vfs.stat(target, { cred: ctx.cred });
18599
+ } catch {
18600
+ stat2 = null;
18601
+ }
18602
+ if (stat2?.isFile()) {
18603
+ files.add(target);
18604
+ continue;
18605
+ }
18606
+ if (stat2?.isDirectory()) {
18607
+ walk(target, byName, basename(target) === "test");
18608
+ continue;
18609
+ }
18610
+ if (/[*?]/.test(pattern)) {
18611
+ const matcher = globToRegExp(target);
18612
+ const before = files.size;
18613
+ walk(ctx.cwd, (path) => {
18614
+ if (matcher.test(path)) files.add(path);
18615
+ });
18616
+ if (files.size > before) continue;
18617
+ }
18618
+ missing.push(pattern);
18619
+ }
18620
+ return { files: [...files].sort(), missing };
18621
+ }
18622
+ async function runTestFiles(ctx, args, settings, preloads, env2) {
18623
+ const patterns = [];
18624
+ for (const arg of args) {
18625
+ if (arg.startsWith("--test-reporter=")) settings.reporter = arg.endsWith("=tap") ? "tap" : "spec";
18626
+ else if (arg.startsWith("--test-name-pattern=")) settings.namePatterns.push(arg.slice("--test-name-pattern=".length));
18627
+ else if (arg === "--test-only") settings.only = true;
18628
+ else if (!arg.startsWith("-")) patterns.push(arg);
18629
+ }
18630
+ const { files, missing } = discoverTestFiles(ctx, patterns);
18631
+ if (missing.length > 0) {
18632
+ for (const pattern of missing) ctx.stderr.write(`Could not find '${ctx.path(pattern)}'
18633
+ `);
18634
+ return 1;
18635
+ }
18636
+ if (files.length === 0) {
18637
+ ctx.stderr.write(
18638
+ "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"
18639
+ );
18640
+ return 0;
18641
+ }
18642
+ const childEnv = { ...env2, SANDBOXEDJS_NODE_TEST: JSON.stringify(settings) };
18643
+ let failed = 0;
18644
+ for (const file3 of files) {
18645
+ const child = ctx.kernel.spawn(["node", ...preloads.flatMap((spec) => ["-r", spec]), file3], {
18646
+ cwd: ctx.cwd,
18647
+ env: childEnv,
18648
+ cred: ctx.cred,
18649
+ ppid: ctx.proc.pid,
18650
+ stdout: ctx.stdout,
18651
+ stderr: ctx.stderr
18652
+ });
18653
+ if (await child.wait() !== 0) failed += 1;
18654
+ }
18655
+ if (files.length > 1) {
18656
+ ctx.line(settings.reporter === "tap" ? `# files ${files.length} failed ${failed}` : `\u2139 files ${files.length}, failed ${failed}`);
18657
+ }
18658
+ return failed > 0 ? 1 : 0;
18659
+ }
18660
+ var nodeVersionFile = defineCommand({
18661
+ name: "nvm",
18662
+ path: "/usr/local/bin/nvm",
18663
+ summary: "node version manager (single-version stub)",
18664
+ run(ctx) {
18665
+ const sub = ctx.args[0];
18666
+ if (sub === "current" || sub === "version") {
18667
+ ctx.line(NODE_VERSION);
18668
+ return 0;
18669
+ }
18670
+ if (sub === "ls" || sub === "list") {
18671
+ ctx.line(`-> ${NODE_VERSION}`);
18672
+ return 0;
18673
+ }
18674
+ ctx.line(`nvm: only ${NODE_VERSION} is available in this container`);
18675
+ return 0;
18676
+ }
18677
+ });
18678
+ function nodeCommands() {
18679
+ return [node, nodeVersionFile];
18680
+ }
18681
+
18682
+ // src/python/python.ts
18683
+ init_path();
18684
+
18685
+ // src/python/host-abi.ts
18686
+ var SBX_HOST_ABI_VERSION = 1;
18687
+ var SBX_REQUEST_HEADER_BYTES = 16;
18688
+ var SBX_RESPONSE_HEADER_BYTES = 20;
18689
+ var Op = {
18690
+ handshake: 0,
18691
+ openat: 1,
18692
+ close: 2,
18693
+ read: 3,
18694
+ write: 4,
18695
+ pread: 5,
18696
+ pwrite: 6,
18697
+ lseek: 7,
18698
+ fstat: 8,
18699
+ statat: 9,
18700
+ ftruncate: 10,
18701
+ renameat: 11,
18702
+ unlinkat: 12,
18703
+ mkdirat: 13,
18704
+ readlinkat: 14,
18705
+ symlinkat: 15,
18706
+ getdents: 16,
18707
+ fsync: 17,
18708
+ dup: 256,
18709
+ dup2: 257,
18710
+ get_flags: 258,
18711
+ set_flags: 259,
18712
+ pipe: 512,
18713
+ poll: 768,
18714
+ clock_gettime: 1024,
18715
+ sleep: 1025,
18716
+ getpid: 1280,
18717
+ getcwd: 1281,
18718
+ chdir: 1282,
18719
+ environ: 1283,
17914
18720
  getrandom: 1536,
17915
18721
  socket: 1792,
17916
18722
  bind: 1793,
@@ -18346,7 +19152,7 @@ var wheels_default = {
18346
19152
 
18347
19153
  // package.json
18348
19154
  var package_default = {
18349
- version: "0.1.73"};
19155
+ version: "0.1.74"};
18350
19156
 
18351
19157
  // src/python/config.ts
18352
19158
  function runtimeModuleUrl() {
@@ -22317,7 +23123,7 @@ var pyodideModule = null;
22317
23123
  var indexUrl;
22318
23124
  var moduleUrl;
22319
23125
  var DEFAULT_BROWSER_INDEX_URL = "https://cdn.jsdelivr.net/pyodide/v0.28.3/full/";
22320
- var isNode2 = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
23126
+ var isNode3 = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
22321
23127
  function configureCPython(options = {}) {
22322
23128
  if (options.indexURL !== void 0) indexUrl = options.indexURL;
22323
23129
  if (options.moduleURL !== void 0) {
@@ -22333,7 +23139,7 @@ function importPyodide() {
22333
23139
  /* webpackIgnore: true */
22334
23140
  moduleUrl
22335
23141
  );
22336
- } else if (isNode2) {
23142
+ } else if (isNode3) {
22337
23143
  pyodideModule = nodeOnlyModule("pyodide");
22338
23144
  } else {
22339
23145
  pyodideModule = import(
@@ -22347,7 +23153,7 @@ function importPyodide() {
22347
23153
  }
22348
23154
  async function resolveIndexUrl() {
22349
23155
  if (indexUrl) return indexUrl;
22350
- if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
23156
+ if (!isNode3) return DEFAULT_BROWSER_INDEX_URL;
22351
23157
  try {
22352
23158
  const { createRequire } = await nodeBuiltin("module");
22353
23159
  const path = await nodeBuiltin("path");
@@ -24286,7 +25092,7 @@ function renameShadowedExports(source) {
24286
25092
  (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
24287
25093
  );
24288
25094
  if (!isModule) return null;
24289
- const programScope = { bindsExports: hoistedNames(body).has(NAME), parent: null };
25095
+ const programScope = { bindsExports: hoistedNames2(body).has(NAME), parent: null };
24290
25096
  if (!programScope.bindsExports) return null;
24291
25097
  const targets = [];
24292
25098
  let bail = false;
@@ -24302,24 +25108,24 @@ function renameShadowedExports(source) {
24302
25108
  function visit(node2, scope) {
24303
25109
  if (bail) return;
24304
25110
  let childScope = scope;
24305
- let skip = NOTHING;
25111
+ let skip = NOTHING2;
24306
25112
  switch (node2.type) {
24307
25113
  case "FunctionDeclaration":
24308
25114
  case "FunctionExpression":
24309
25115
  case "ArrowFunctionExpression": {
24310
25116
  const names = /* @__PURE__ */ new Set();
24311
- for (const param of node2.params ?? []) collectPattern(param, names);
25117
+ for (const param of node2.params ?? []) collectPattern2(param, names);
24312
25118
  if (isExports(node2.id) && node2.type === "FunctionExpression") names.add(NAME);
24313
25119
  const fnBody = node2.body;
24314
25120
  if (fnBody?.type === "BlockStatement") {
24315
- for (const name of hoistedNames(fnBody.body)) names.add(name);
25121
+ for (const name of hoistedNames2(fnBody.body)) names.add(name);
24316
25122
  }
24317
25123
  childScope = { bindsExports: names.has(NAME), parent: scope };
24318
25124
  break;
24319
25125
  }
24320
25126
  case "CatchClause": {
24321
25127
  const names = /* @__PURE__ */ new Set();
24322
- if (node2.param) collectPattern(node2.param, names);
25128
+ if (node2.param) collectPattern2(node2.param, names);
24323
25129
  childScope = { bindsExports: names.has(NAME), parent: scope };
24324
25130
  break;
24325
25131
  }
@@ -24333,7 +25139,7 @@ function renameShadowedExports(source) {
24333
25139
  case "StaticBlock":
24334
25140
  if (node2 !== ast.body) {
24335
25141
  childScope = {
24336
- bindsExports: blockNames(node2.body).has(NAME),
25142
+ bindsExports: blockNames2(node2.body).has(NAME),
24337
25143
  parent: scope
24338
25144
  };
24339
25145
  }
@@ -24345,7 +25151,7 @@ function renameShadowedExports(source) {
24345
25151
  if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
24346
25152
  const names = /* @__PURE__ */ new Set();
24347
25153
  for (const declarator of head2.declarations) {
24348
- collectPattern(declarator.id, names);
25154
+ collectPattern2(declarator.id, names);
24349
25155
  }
24350
25156
  childScope = { bindsExports: names.has(NAME), parent: scope };
24351
25157
  }
@@ -24369,7 +25175,7 @@ function renameShadowedExports(source) {
24369
25175
  case "MemberExpression":
24370
25176
  case "MethodDefinition":
24371
25177
  case "PropertyDefinition":
24372
- skip = node2.computed ? NOTHING : PROPERTY;
25178
+ skip = node2.computed ? NOTHING2 : PROPERTY2;
24373
25179
  break;
24374
25180
  case "Property":
24375
25181
  if (node2.computed) break;
@@ -24380,19 +25186,19 @@ function renameShadowedExports(source) {
24380
25186
  }
24381
25187
  return;
24382
25188
  }
24383
- skip = PROPERTY;
25189
+ skip = PROPERTY2;
24384
25190
  break;
24385
25191
  case "LabeledStatement":
24386
25192
  case "BreakStatement":
24387
25193
  case "ContinueStatement":
24388
- skip = LABEL;
25194
+ skip = LABEL2;
24389
25195
  break;
24390
25196
  }
24391
25197
  for (const [key, value] of Object.entries(node2)) {
24392
25198
  if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
24393
25199
  if (Array.isArray(value)) {
24394
- for (const item of value) if (isNode3(item)) visit(item, childScope);
24395
- } else if (isNode3(value)) {
25200
+ for (const item of value) if (isNode4(item)) visit(item, childScope);
25201
+ } else if (isNode4(value)) {
24396
25202
  visit(value, childScope);
24397
25203
  }
24398
25204
  }
@@ -24404,73 +25210,73 @@ function resolvesToProgram(scope) {
24404
25210
  }
24405
25211
  return false;
24406
25212
  }
24407
- function hoistedNames(body) {
24408
- const names = blockNames(body);
24409
- collectVars(body, names);
25213
+ function hoistedNames2(body) {
25214
+ const names = blockNames2(body);
25215
+ collectVars2(body, names);
24410
25216
  return names;
24411
25217
  }
24412
- function blockNames(body) {
25218
+ function blockNames2(body) {
24413
25219
  const names = /* @__PURE__ */ new Set();
24414
25220
  for (const node2 of body ?? []) {
24415
25221
  if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
24416
25222
  for (const declarator of node2.declarations) {
24417
- collectPattern(declarator.id, names);
25223
+ collectPattern2(declarator.id, names);
24418
25224
  }
24419
- } else if (node2.type === "ClassDeclaration" && isNode3(node2.id)) {
25225
+ } else if (node2.type === "ClassDeclaration" && isNode4(node2.id)) {
24420
25226
  names.add(node2.id.name);
24421
- } else if (node2.type === "FunctionDeclaration" && isNode3(node2.id)) {
25227
+ } else if (node2.type === "FunctionDeclaration" && isNode4(node2.id)) {
24422
25228
  names.add(node2.id.name);
24423
25229
  }
24424
25230
  }
24425
25231
  return names;
24426
25232
  }
24427
- function collectVars(nodes, names) {
25233
+ function collectVars2(nodes, names) {
24428
25234
  if (Array.isArray(nodes)) {
24429
- for (const item of nodes) collectVars(item, names);
25235
+ for (const item of nodes) collectVars2(item, names);
24430
25236
  return;
24431
25237
  }
24432
- if (!isNode3(nodes)) return;
25238
+ if (!isNode4(nodes)) return;
24433
25239
  const node2 = nodes;
24434
25240
  if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
24435
- if (isNode3(node2.id)) names.add(node2.id.name);
25241
+ if (isNode4(node2.id)) names.add(node2.id.name);
24436
25242
  return;
24437
25243
  }
24438
25244
  if (node2.type === "VariableDeclaration" && node2.kind === "var") {
24439
25245
  for (const declarator of node2.declarations) {
24440
- collectPattern(declarator.id, names);
25246
+ collectPattern2(declarator.id, names);
24441
25247
  }
24442
25248
  }
24443
25249
  for (const [key, value] of Object.entries(node2)) {
24444
25250
  if (key === "type" || key === "start" || key === "end") continue;
24445
- collectVars(value, names);
25251
+ collectVars2(value, names);
24446
25252
  }
24447
25253
  }
24448
- function collectPattern(node2, names) {
24449
- if (!isNode3(node2)) return;
25254
+ function collectPattern2(node2, names) {
25255
+ if (!isNode4(node2)) return;
24450
25256
  switch (node2.type) {
24451
25257
  case "Identifier":
24452
25258
  names.add(node2.name);
24453
25259
  return;
24454
25260
  case "ObjectPattern":
24455
25261
  for (const property of node2.properties) {
24456
- collectPattern(property.value ?? property.argument, names);
25262
+ collectPattern2(property.value ?? property.argument, names);
24457
25263
  }
24458
25264
  return;
24459
25265
  case "ArrayPattern":
24460
- for (const element of node2.elements) collectPattern(element, names);
25266
+ for (const element of node2.elements) collectPattern2(element, names);
24461
25267
  return;
24462
25268
  case "AssignmentPattern":
24463
- collectPattern(node2.left, names);
25269
+ collectPattern2(node2.left, names);
24464
25270
  return;
24465
25271
  case "RestElement":
24466
- collectPattern(node2.argument, names);
25272
+ collectPattern2(node2.argument, names);
24467
25273
  return;
24468
25274
  default:
24469
25275
  return;
24470
25276
  }
24471
25277
  }
24472
25278
  function isExports(value) {
24473
- return isNode3(value) && value.type === "Identifier" && value.name === NAME;
25279
+ return isNode4(value) && value.type === "Identifier" && value.name === NAME;
24474
25280
  }
24475
25281
  function freshName(source) {
24476
25282
  let name = "__sandboxedjs_exports";
@@ -24478,12 +25284,12 @@ function freshName(source) {
24478
25284
  while (source.includes(name)) name = `__sandboxedjs_exports${++suffix}`;
24479
25285
  return name;
24480
25286
  }
24481
- function isNode3(value) {
25287
+ function isNode4(value) {
24482
25288
  return typeof value === "object" && value !== null && typeof value.type === "string";
24483
25289
  }
24484
- var NOTHING = [];
24485
- var PROPERTY = ["property", "key"];
24486
- var LABEL = ["label"];
25290
+ var NOTHING2 = [];
25291
+ var PROPERTY2 = ["property", "key"];
25292
+ var LABEL2 = ["label"];
24487
25293
 
24488
25294
  // src/pkg/index.ts
24489
25295
  init_path();
@@ -25229,752 +26035,356 @@ var ContainerFs = class {
25229
26035
  this.vfs.symlink(target, clean(link));
25230
26036
  }
25231
26037
  async readlink(path) {
25232
- return this.vfs.readlink(clean(path));
25233
- }
25234
- async realpath(path) {
25235
- return this.vfs.realpath(clean(path));
25236
- }
25237
- async chmod(path, mode) {
25238
- this.vfs.chmod(clean(path), mode);
25239
- }
25240
- async chown(path, uid, gid) {
25241
- this.vfs.chown(clean(path), uid, gid, ROOT_CRED);
25242
- }
25243
- /** Every path beneath `root`, depth-first. */
25244
- async walk(root = "/") {
25245
- return [...this.vfs.walk(clean(root))];
25246
- }
25247
- /** Total bytes and file counts, as `df` reports them. */
25248
- async usage(root = "/") {
25249
- return this.vfs.usage(clean(root));
25250
- }
25251
- /** Read many files at once, keyed by path. */
25252
- async readAll(paths) {
25253
- const out = {};
25254
- for (const path of paths) out[path] = this.vfs.readText(clean(path));
25255
- return out;
25256
- }
25257
- /** Write a whole map of files, creating parents. */
25258
- async writeAll(files, opts = {}) {
25259
- for (const [key, value] of Object.entries(files)) {
25260
- await this.writeFile(resolve(opts.cwd ?? "/", key), value);
25261
- }
25262
- }
25263
- };
25264
-
25265
- // src/container/session.ts
25266
- var Session = class {
25267
- constructor(kernel, init) {
25268
- this.kernel = kernel;
25269
- this.proc = kernel.procs.create({
25270
- argv: ["-sh"],
25271
- cwd: init.cwd,
25272
- env: init.env,
25273
- cred: init.cred,
25274
- ppid: 1,
25275
- kind: "shell",
25276
- tty: "pts/0"
25277
- });
25278
- this.shell = new Shell({
25279
- kernel,
25280
- proc: this.proc,
25281
- cwd: init.cwd,
25282
- env: init.env,
25283
- cred: init.cred,
25284
- scriptName: "sh",
25285
- options: { interactive: true }
25286
- });
25287
- this.hooks = init.hooks ?? {};
25288
- }
25289
- kernel;
25290
- shell;
25291
- proc;
25292
- closed = false;
25293
- hooks;
25294
- get cwd() {
25295
- return this.shell.cwd;
25296
- }
25297
- get env() {
25298
- return this.shell.vars.environment();
25299
- }
25300
- get history() {
25301
- return this.shell.history;
25302
- }
25303
- /** Run a command line, keeping every side effect for the next call. */
25304
- async run(command, opts = {}) {
25305
- if (this.closed) throw new Error("session is closed");
25306
- const combined = [];
25307
- const decoder9 = new TextDecoder();
25308
- const stdout = new BufferSink((chunk) => {
25309
- const text2 = decoder9.decode(chunk, { stream: true });
25310
- combined.push(text2);
25311
- opts.onStdout?.(text2);
25312
- this.hooks.onStdout?.(text2);
25313
- });
25314
- const stderr = new BufferSink((chunk) => {
25315
- const text2 = decoder9.decode(chunk, { stream: true });
25316
- combined.push(text2);
25317
- opts.onStderr?.(text2);
25318
- this.hooks.onStderr?.(text2);
25319
- });
25320
- if (opts.tty) {
25321
- stdout.isTTY = true;
25322
- stderr.isTTY = true;
25323
- stdout.columns = opts.columns ?? 80;
25324
- stdout.rows = opts.rows ?? 24;
25325
- }
25326
- const stdin = opts.stdin === void 0 ? new NullInput() : typeof opts.stdin === "string" || opts.stdin instanceof Uint8Array ? Pipe.from(opts.stdin) : opts.stdin;
25327
- this.shell.history.push(command);
25328
- let timer;
25329
- if (opts.timeoutMs !== void 0) {
25330
- timer = setTimeout(() => this.proc.deliver("SIGKILL"), opts.timeoutMs);
25331
- }
25332
- let exitCode;
25333
- try {
25334
- exitCode = await this.shell.execute(command, { stdin, stdout, stderr });
25335
- } finally {
25336
- if (timer) clearTimeout(timer);
25337
- }
25338
- return { exitCode, stdout: stdout.text(), stderr: stderr.text(), output: combined.join("") };
25339
- }
25340
- /** Stream a long-running command; resolves when it exits. */
25341
- async stream(command, handlers = {}) {
25342
- const result = await this.run(command, handlers);
25343
- return result.exitCode;
25344
- }
25345
- /** Feed the session an arbitrary output stream, for terminal integration. */
25346
- async pipeTo(command, stdout, stderr, stdin) {
25347
- return this.shell.execute(command, { stdin: stdin ?? new NullInput(), stdout, stderr });
25348
- }
25349
- /** True when the last command asked the shell to exit. */
25350
- get isExiting() {
25351
- return this.shell.isExiting;
25352
- }
25353
- close() {
25354
- if (this.closed) return;
25355
- this.closed = true;
25356
- this.proc.exit(0);
25357
- this.kernel.procs.remove(this.proc.pid);
25358
- }
25359
- };
25360
-
25361
- // src/node/node-child-process-bridge.ts
25362
- var KernelChildProcess = class {
25363
- pid;
25364
- command;
25365
- args;
25366
- parentPid;
25367
- state = "running";
25368
- exitCode;
25369
- stdout = "";
25370
- stderr = "";
25371
- listeners = /* @__PURE__ */ new Map();
25372
- kernel;
25373
- cred;
25374
- cwd;
25375
- env;
25376
- stdin = new Pipe();
25377
- process;
25378
- started = false;
25379
- cancelled = false;
25380
- constructor(kernel, cred, config2, pid) {
25381
- this.stdin.interactive = true;
25382
- if (config2.inheritStdio) this.stdin.isTTY = true;
25383
- if (config2.stdinIgnored) this.stdin.end();
25384
- this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
25385
- this.kernel = kernel;
25386
- this.cred = cred;
25387
- this.pid = pid;
25388
- this.command = config2.command;
25389
- this.args = config2.args?.slice() ?? [];
25390
- this.parentPid = config2.parentPid;
25391
- this.cwd = config2.cwd ?? "/";
25392
- this.env = { ...config2.env };
25393
- }
25394
- on(event, listener) {
25395
- let listeners = this.listeners.get(event);
25396
- if (!listeners) {
25397
- listeners = /* @__PURE__ */ new Set();
25398
- this.listeners.set(event, listeners);
25399
- }
25400
- listeners.add(listener);
25401
- return this;
25402
- }
25403
- emit(event, ...args) {
25404
- for (const listener of this.listeners.get(event) ?? []) listener(...args);
25405
- }
25406
- exec() {
25407
- if (this.started) return;
25408
- this.started = true;
25409
- queueMicrotask(() => {
25410
- if (this.cancelled) {
25411
- this.finish(143);
25412
- return;
25413
- }
25414
- const stdout = new ChildOutput(this, "stdout");
25415
- const stderr = new ChildOutput(this, "stderr");
25416
- try {
25417
- this.process = this.kernel.spawn([this.command, ...this.args], {
25418
- cwd: this.cwd,
25419
- env: this.env,
25420
- cred: this.cred,
25421
- stdin: this.stdin,
25422
- stdout,
25423
- stderr,
25424
- ppid: 1
25425
- });
25426
- void this.process.wait().then((code) => {
25427
- stdout.end();
25428
- stderr.end();
25429
- this.finish(code);
25430
- });
25431
- } catch (error) {
25432
- stderr.write(error instanceof Error ? error.message : String(error));
25433
- stderr.end();
25434
- stdout.end();
25435
- this.finish(1);
25436
- }
25437
- });
25438
- }
25439
- sendStdin(data) {
25440
- try {
25441
- this.stdin.write(data);
25442
- } catch {
25443
- }
25444
- }
25445
- /** Signal EOF, so a child reading stdin to the end can finish. */
25446
- endStdin() {
25447
- try {
25448
- this.stdin.end();
25449
- } catch {
25450
- }
25451
- }
25452
- kill(signal = "SIGTERM") {
25453
- if (this.process) {
25454
- this.process.deliver(signal);
25455
- return;
25456
- }
25457
- this.cancelled = true;
26038
+ return this.vfs.readlink(clean(path));
25458
26039
  }
25459
- finish(code) {
25460
- if (this.state === "exited") return;
25461
- this.state = "exited";
25462
- this.exitCode = code;
25463
- this.emit("exit", code);
26040
+ async realpath(path) {
26041
+ return this.vfs.realpath(clean(path));
25464
26042
  }
25465
- append(stream, text2) {
25466
- this[stream] += text2;
25467
- this.emit(stream, text2);
26043
+ async chmod(path, mode) {
26044
+ this.vfs.chmod(clean(path), mode);
25468
26045
  }
25469
- };
25470
- var ChildOutput = class {
25471
- constructor(child, stream) {
25472
- this.child = child;
25473
- this.stream = stream;
26046
+ async chown(path, uid, gid) {
26047
+ this.vfs.chown(clean(path), uid, gid, ROOT_CRED);
25474
26048
  }
25475
- child;
25476
- stream;
25477
- isTTY = false;
25478
- closedState = false;
25479
- decoder = new TextDecoder();
25480
- get closed() {
25481
- return this.closedState;
26049
+ /** Every path beneath `root`, depth-first. */
26050
+ async walk(root = "/") {
26051
+ return [...this.vfs.walk(clean(root))];
25482
26052
  }
25483
- write(data) {
25484
- if (this.closedState) return;
25485
- const text2 = typeof data === "string" ? data : this.decoder.decode(data, { stream: true });
25486
- if (text2) this.child.append(this.stream, text2);
26053
+ /** Total bytes and file counts, as `df` reports them. */
26054
+ async usage(root = "/") {
26055
+ return this.vfs.usage(clean(root));
25487
26056
  }
25488
- end() {
25489
- if (this.closedState) return;
25490
- this.closedState = true;
25491
- const tail2 = this.decoder.decode();
25492
- if (tail2) this.child.append(this.stream, tail2);
26057
+ /** Read many files at once, keyed by path. */
26058
+ async readAll(paths) {
26059
+ const out = {};
26060
+ for (const path of paths) out[path] = this.vfs.readText(clean(path));
26061
+ return out;
25493
26062
  }
25494
- };
25495
- var nextBridgePid = 1073741824;
25496
- function installNodeChildProcessBridge(pod, kernel, cred) {
25497
- const manager = pod.processManager;
25498
- const originalSpawn = manager.spawn;
25499
- manager.spawn = (config2) => {
25500
- const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
25501
- if (resolved?.kind === "builtin") {
25502
- return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
26063
+ /** Write a whole map of files, creating parents. */
26064
+ async writeAll(files, opts = {}) {
26065
+ for (const [key, value] of Object.entries(files)) {
26066
+ await this.writeFile(resolve(opts.cwd ?? "/", key), value);
25503
26067
  }
25504
- return originalSpawn.call(manager, config2);
25505
- };
25506
- return () => {
25507
- if (manager.spawn === originalSpawn) return;
25508
- manager.spawn = originalSpawn;
25509
- };
25510
- }
25511
-
25512
- // src/node/host-module-tracker.ts
25513
- var HostModuleTracker = class {
25514
- active = 0;
25515
- disposed = false;
25516
- dispose() {
25517
- this.disposed = true;
25518
- }
25519
- wrapped = /* @__PURE__ */ new WeakMap();
25520
- originals = /* @__PURE__ */ new WeakMap();
25521
- pending = () => this.active;
25522
- unwrap = (value) => value && (typeof value === "object" || typeof value === "function") ? this.originals.get(value) ?? value : value;
25523
- wrap(value) {
25524
- if (value === null || typeof value !== "object" && typeof value !== "function") return value;
25525
- const object = value;
25526
- if (Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;
25527
- if (this.wrapped.has(object)) return this.wrapped.get(object);
25528
- const tracker = this;
25529
- const proxy = new Proxy(object, {
25530
- // CommonJS native loaders re-export by assigning exports back onto the
25531
- // binding object. Never store a process-owned proxy in a shared module.
25532
- set(target, key, member) {
25533
- return Reflect.set(target, key, tracker.unwrap(member), target);
25534
- },
25535
- defineProperty(target, key, descriptor) {
25536
- return Reflect.defineProperty(target, key, "value" in descriptor ? { ...descriptor, value: tracker.unwrap(descriptor.value) } : descriptor);
25537
- },
25538
- get(target, key) {
25539
- const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
25540
- const member = Reflect.get(target, key, target);
25541
- if (descriptor && !descriptor.configurable && "value" in descriptor && !descriptor.writable) return member;
25542
- return typeof member === "function" ? tracker.wrap(member) : member;
25543
- },
25544
- apply(target, receiver, args) {
25545
- const result = Reflect.apply(target, tracker.unwrap(receiver), args.map(tracker.unwrap));
25546
- if (result && typeof result.then === "function") {
25547
- tracker.active++;
25548
- return Promise.resolve(result).then(
25549
- (value2) => {
25550
- tracker.active--;
25551
- return tracker.disposed ? new Promise(() => {
25552
- }) : tracker.wrap(value2);
25553
- },
25554
- (error) => {
25555
- tracker.active--;
25556
- if (tracker.disposed) return new Promise(() => {
25557
- });
25558
- throw error;
25559
- }
25560
- );
25561
- }
25562
- return tracker.wrap(result);
25563
- },
25564
- construct(target, args, newTarget) {
25565
- return tracker.wrap(Reflect.construct(target, args.map(tracker.unwrap), tracker.unwrap(newTarget)));
25566
- }
25567
- });
25568
- this.wrapped.set(object, proxy);
25569
- this.originals.set(proxy, object);
25570
- return proxy;
25571
26068
  }
25572
26069
  };
25573
26070
 
25574
- // src/node/commonjs-engine.ts
25575
- init_path();
25576
- var HELPERS = {
25577
- /** Import a specifier and return an ES-module-shaped namespace. */
25578
- import: "__sbxImport",
25579
- /** `import(...)`, returning a promise of a namespace. */
25580
- dynamic: "__sbxDynamicImport",
25581
- /** `export * from` — copy live bindings onto `exports`. */
25582
- exportAll: "__sbxExportAll",
25583
- /** `import.meta`. */
25584
- meta: "__sbxMeta",
25585
- /**
25586
- * The exports object, under a name of the engine's choosing.
25587
- *
25588
- * An ES module is free to declare its own top-level `exports`, `require` or
25589
- * `__dirname` — they are ordinary identifiers there, and real packages use
25590
- * all three (`const require = createRequire(import.meta.url)` is close to
25591
- * idiomatic). Naming the wrapper's binding something no source would write
25592
- * removes that entire class of collision instead of patching it up after a
25593
- * `SyntaxError`.
25594
- */
25595
- exports: "__sbxExports"
25596
- };
25597
- var DEFAULT_LOCAL = "__sbxDefault";
25598
- var MAYBE_ESM = /(^|[\s;}(])(?:import|export)(?:[\s({[*"']|$)|\bimport\s*\.\s*meta\b/;
25599
- function looksLikeEsm(source) {
25600
- return MAYBE_ESM.test(source);
25601
- }
25602
- function transformEsm(source, filename = "module.js") {
25603
- if (!looksLikeEsm(source)) return null;
25604
- let ast;
25605
- try {
25606
- ast = parseModule(source, filename);
25607
- } catch {
25608
- return null;
25609
- }
25610
- const body = ast.body;
25611
- const hasModuleSyntax = body.some(
25612
- (node2) => node2.type.startsWith("Import") || node2.type.startsWith("Export")
25613
- );
25614
- const usesImportMeta = !hasModuleSyntax && containsImportMeta(ast);
25615
- const esm = hasModuleSyntax || usesImportMeta;
25616
- if (!esm && !containsImportExpression(ast)) return null;
25617
- const edits = [];
25618
- const prelude = [];
25619
- const importBindings = /* @__PURE__ */ new Map();
25620
- const exportGetters = /* @__PURE__ */ new Map();
25621
- let namespaceCount = 0;
25622
- const namespaceFor = (specifier) => {
25623
- const id = `__sbxNs${namespaceCount++}`;
25624
- prelude.push(`var ${id} = ${HELPERS.import}(${JSON.stringify(specifier)});`);
25625
- return id;
25626
- };
25627
- for (const node2 of body) {
25628
- if (node2.type === "ImportDeclaration") collectImport(node2);
26071
+ // src/container/session.ts
26072
+ var Session = class {
26073
+ constructor(kernel, init) {
26074
+ this.kernel = kernel;
26075
+ this.proc = kernel.procs.create({
26076
+ argv: ["-sh"],
26077
+ cwd: init.cwd,
26078
+ env: init.env,
26079
+ cred: init.cred,
26080
+ ppid: 1,
26081
+ kind: "shell",
26082
+ tty: "pts/0"
26083
+ });
26084
+ this.shell = new Shell({
26085
+ kernel,
26086
+ proc: this.proc,
26087
+ cwd: init.cwd,
26088
+ env: init.env,
26089
+ cred: init.cred,
26090
+ scriptName: "sh",
26091
+ options: { interactive: true }
26092
+ });
26093
+ this.hooks = init.hooks ?? {};
25629
26094
  }
25630
- for (const node2 of body) {
25631
- switch (node2.type) {
25632
- case "ExportNamedDeclaration":
25633
- collectNamedExport(node2);
25634
- break;
25635
- case "ExportDefaultDeclaration":
25636
- collectDefaultExport(node2);
25637
- break;
25638
- case "ExportAllDeclaration":
25639
- collectExportAll(node2);
25640
- break;
25641
- }
26095
+ kernel;
26096
+ shell;
26097
+ proc;
26098
+ closed = false;
26099
+ hooks;
26100
+ get cwd() {
26101
+ return this.shell.cwd;
25642
26102
  }
25643
- rewriteReferences();
25644
- const header = esm ? [
25645
- `Object.defineProperty(${HELPERS.exports}, "__esModule", { value: true });`,
25646
- ...prelude,
25647
- ...[...exportGetters].map(
25648
- ([name, expression]) => `Object.defineProperty(${HELPERS.exports}, ${JSON.stringify(name)}, { enumerable: true, configurable: true, get: function () { return ${expression}; } });`
25649
- )
25650
- ].join("\n") : "";
25651
- return {
25652
- code: header ? `${header}
25653
- ${applyEdits(source, edits)}` : applyEdits(source, edits),
25654
- esm,
25655
- topLevelAwait: hasTopLevelAwait(ast)
25656
- };
25657
- function collectImport(node2) {
25658
- const specifier = node2.source.value;
25659
- const specifiers = node2.specifiers ?? [];
25660
- const id = namespaceFor(specifier);
25661
- for (const entry of specifiers) {
25662
- const local = entry.local.name ?? "";
25663
- if (entry.type === "ImportDefaultSpecifier") {
25664
- importBindings.set(local, `${id}.default`);
25665
- } else if (entry.type === "ImportNamespaceSpecifier") {
25666
- importBindings.set(local, id);
25667
- } else {
25668
- const imported = entry.imported;
25669
- const name = imported.type === "Identifier" ? imported.name : imported.value;
25670
- importBindings.set(local, `${id}[${JSON.stringify(name)}]`);
25671
- }
25672
- }
25673
- drop(node2);
26103
+ get env() {
26104
+ return this.shell.vars.environment();
25674
26105
  }
25675
- function collectNamedExport(node2) {
25676
- const declaration = node2.declaration;
25677
- if (declaration) {
25678
- for (const name of declaredNames(declaration)) exportGetters.set(name, name);
25679
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25680
- return;
26106
+ get history() {
26107
+ return this.shell.history;
26108
+ }
26109
+ /** Run a command line, keeping every side effect for the next call. */
26110
+ async run(command, opts = {}) {
26111
+ if (this.closed) throw new Error("session is closed");
26112
+ const combined = [];
26113
+ const decoder9 = new TextDecoder();
26114
+ const stdout = new BufferSink((chunk) => {
26115
+ const text2 = decoder9.decode(chunk, { stream: true });
26116
+ combined.push(text2);
26117
+ opts.onStdout?.(text2);
26118
+ this.hooks.onStdout?.(text2);
26119
+ });
26120
+ const stderr = new BufferSink((chunk) => {
26121
+ const text2 = decoder9.decode(chunk, { stream: true });
26122
+ combined.push(text2);
26123
+ opts.onStderr?.(text2);
26124
+ this.hooks.onStderr?.(text2);
26125
+ });
26126
+ const outGate = new DetachableOutput(stdout, (text2) => this.hooks.onStdout?.(text2));
26127
+ const errGate = new DetachableOutput(stderr, (text2) => this.hooks.onStderr?.(text2));
26128
+ if (opts.tty) {
26129
+ outGate.isTTY = true;
26130
+ errGate.isTTY = true;
26131
+ outGate.columns = opts.columns ?? 80;
26132
+ outGate.rows = opts.rows ?? 24;
25681
26133
  }
25682
- const source_ = node2.source;
25683
- const id = source_ ? namespaceFor(source_.value) : null;
25684
- for (const entry of node2.specifiers ?? []) {
25685
- const local = nameOf(entry.local);
25686
- const exported = nameOf(entry.exported);
25687
- const read = id ? `${id}[${JSON.stringify(local)}]` : importBindings.get(local) ?? local;
25688
- exportGetters.set(exported, read);
26134
+ const stdin = opts.stdin === void 0 ? new NullInput() : typeof opts.stdin === "string" || opts.stdin instanceof Uint8Array ? Pipe.from(opts.stdin) : opts.stdin;
26135
+ this.shell.history.push(command);
26136
+ let timer;
26137
+ if (opts.timeoutMs !== void 0) {
26138
+ timer = setTimeout(() => this.proc.deliver("SIGKILL"), opts.timeoutMs);
25689
26139
  }
25690
- drop(node2);
25691
- }
25692
- function collectDefaultExport(node2) {
25693
- const declaration = node2.declaration;
25694
- const isDeclaration = declaration.type === "FunctionDeclaration" || declaration.type === "ClassDeclaration";
25695
- if (isDeclaration) {
25696
- const named = declaration.id;
25697
- if (named) {
25698
- exportGetters.set("default", named.name);
25699
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25700
- return;
25701
- }
25702
- const keyword = declaration.type === "FunctionDeclaration" ? "function" : "class";
25703
- const keywordEnd = source.indexOf(keyword, declaration.start) + keyword.length;
25704
- exportGetters.set("default", DEFAULT_LOCAL);
25705
- edits.push({ start: node2.start, end: declaration.start, text: "" });
25706
- edits.push({ start: keywordEnd, end: keywordEnd, text: ` ${DEFAULT_LOCAL}` });
25707
- return;
26140
+ let exitCode;
26141
+ try {
26142
+ exitCode = await this.shell.execute(command, { stdin, stdout: outGate, stderr: errGate });
26143
+ } finally {
26144
+ if (timer) clearTimeout(timer);
26145
+ outGate.detach();
26146
+ errGate.detach();
25708
26147
  }
25709
- exportGetters.set("default", DEFAULT_LOCAL);
25710
- edits.push({ start: node2.start, end: declaration.start, text: `var ${DEFAULT_LOCAL} = ` });
25711
- edits.push({ start: node2.end, end: node2.end, text: ";" });
26148
+ return { exitCode, stdout: stdout.text(), stderr: stderr.text(), output: combined.join("") };
25712
26149
  }
25713
- function collectExportAll(node2) {
25714
- const id = namespaceFor(node2.source.value);
25715
- const exported = node2.exported;
25716
- if (exported) exportGetters.set(nameOf(exported), id);
25717
- else prelude.push(`${HELPERS.exportAll}(${HELPERS.exports}, ${id});`);
25718
- drop(node2);
26150
+ /** Stream a long-running command; resolves when it exits. */
26151
+ async stream(command, handlers = {}) {
26152
+ const result = await this.run(command, handlers);
26153
+ return result.exitCode;
25719
26154
  }
25720
- function drop(node2) {
25721
- edits.push({ start: node2.start, end: node2.end, text: "" });
26155
+ /** Feed the session an arbitrary output stream, for terminal integration. */
26156
+ async pipeTo(command, stdout, stderr, stdin) {
26157
+ return this.shell.execute(command, { stdin: stdin ?? new NullInput(), stdout, stderr });
25722
26158
  }
25723
- function rewriteReferences() {
25724
- const programScope = { names: new Set(importBindings.keys()), parent: null };
25725
- visit(ast, programScope, true);
25726
- function visit(node2, scope, isProgram = false) {
25727
- let childScope = scope;
25728
- let skip = NOTHING2;
25729
- switch (node2.type) {
25730
- case "ImportDeclaration":
25731
- case "ExportAllDeclaration":
25732
- return;
25733
- case "ExportNamedDeclaration":
25734
- if (!node2.declaration) return;
25735
- break;
25736
- case "FunctionDeclaration":
25737
- case "FunctionExpression":
25738
- case "ArrowFunctionExpression": {
25739
- const names = /* @__PURE__ */ new Set();
25740
- for (const param of node2.params ?? []) collectPattern2(param, names);
25741
- const id = node2.id;
25742
- if (id && node2.type === "FunctionExpression") names.add(id.name);
25743
- const fnBody = node2.body;
25744
- if (fnBody?.type === "BlockStatement") {
25745
- for (const name of hoistedNames2(fnBody.body)) names.add(name);
25746
- }
25747
- childScope = { names, parent: scope };
25748
- break;
25749
- }
25750
- case "CatchClause": {
25751
- const names = /* @__PURE__ */ new Set();
25752
- if (node2.param) collectPattern2(node2.param, names);
25753
- childScope = { names, parent: scope };
25754
- break;
25755
- }
25756
- case "ClassExpression": {
25757
- const id = node2.id;
25758
- if (id) childScope = { names: /* @__PURE__ */ new Set([id.name]), parent: scope };
25759
- break;
25760
- }
25761
- case "BlockStatement":
25762
- case "StaticBlock":
25763
- if (!isProgram) {
25764
- childScope = { names: blockNames2(node2.body), parent: scope };
25765
- }
25766
- break;
25767
- case "ForStatement":
25768
- case "ForInStatement":
25769
- case "ForOfStatement": {
25770
- const head2 = node2.init ?? node2.left;
25771
- if (head2?.type === "VariableDeclaration" && head2.kind !== "var") {
25772
- const names = /* @__PURE__ */ new Set();
25773
- for (const declarator of head2.declarations) {
25774
- collectPattern2(declarator.id, names);
25775
- }
25776
- childScope = { names, parent: scope };
25777
- }
25778
- break;
25779
- }
25780
- case "MetaProperty":
25781
- edits.push({ start: node2.start, end: node2.end, text: HELPERS.meta });
25782
- return;
25783
- case "ImportExpression": {
25784
- const argument = node2.source;
25785
- edits.push({ start: node2.start, end: argument.start, text: `${HELPERS.dynamic}(` });
25786
- visit(argument, childScope);
25787
- edits.push({ start: argument.end, end: node2.end, text: ")" });
25788
- return;
25789
- }
25790
- case "Identifier": {
25791
- const replacement = lookup(node2.name, scope);
25792
- if (replacement) edits.push({ start: node2.start, end: node2.end, text: replacement });
25793
- return;
25794
- }
25795
- case "MemberExpression":
25796
- case "MethodDefinition":
25797
- case "PropertyDefinition":
25798
- skip = node2.computed ? NOTHING2 : PROPERTY2;
25799
- break;
25800
- case "Property": {
25801
- if (node2.computed) break;
25802
- if (node2.shorthand) {
25803
- const value = node2.value;
25804
- if (value.type === "Identifier") {
25805
- const replacement = lookup(value.name, scope);
25806
- if (replacement) {
25807
- edits.push({ start: value.start, end: value.end, text: `${value.name}: ${replacement}` });
25808
- return;
25809
- }
25810
- }
25811
- break;
25812
- }
25813
- skip = PROPERTY2;
25814
- break;
25815
- }
25816
- case "LabeledStatement":
25817
- case "BreakStatement":
25818
- case "ContinueStatement":
25819
- skip = LABEL2;
25820
- break;
25821
- }
25822
- for (const [key, value] of Object.entries(node2)) {
25823
- if (key === "type" || key === "start" || key === "end" || skip.includes(key)) continue;
25824
- if (Array.isArray(value)) {
25825
- for (const item of value) if (isNode4(item)) visit(item, childScope);
25826
- } else if (isNode4(value)) {
25827
- visit(value, childScope);
25828
- }
25829
- }
25830
- }
25831
- function lookup(name, scope) {
25832
- for (let current = scope; current; current = current.parent) {
25833
- if (current.names.has(name)) {
25834
- return current.parent === null ? importBindings.get(name) ?? null : null;
25835
- }
25836
- }
25837
- return null;
25838
- }
26159
+ /** True when the last command asked the shell to exit. */
26160
+ get isExiting() {
26161
+ return this.shell.isExiting;
26162
+ }
26163
+ close() {
26164
+ if (this.closed) return;
26165
+ this.closed = true;
26166
+ this.proc.exit(0);
26167
+ this.kernel.procs.remove(this.proc.pid);
25839
26168
  }
25840
- }
25841
- var JSX_EXTENSION = /\.[jt]sx$/;
25842
- var JsxParser = Parser$1.extend(jsx());
25843
- var PARSE_OPTIONS = {
25844
- ecmaVersion: "latest",
25845
- sourceType: "module",
25846
- allowAwaitOutsideFunction: true,
25847
- allowHashBang: true,
25848
- allowReturnOutsideFunction: true
25849
26169
  };
25850
- function parseModule(source, filename) {
25851
- const parser = JSX_EXTENSION.test(filename) ? JsxParser.parse.bind(JsxParser) : parse$1;
25852
- return parser(source, PARSE_OPTIONS);
25853
- }
25854
- function containsImportExpression(node2) {
25855
- if (Array.isArray(node2)) return node2.some(containsImportExpression);
25856
- if (!isNode4(node2)) return false;
25857
- if (node2.type === "ImportExpression") return true;
25858
- for (const [key, value] of Object.entries(node2)) {
25859
- if (key === "type" || key === "start" || key === "end") continue;
25860
- if (containsImportExpression(value)) return true;
26170
+
26171
+ // src/node/node-child-process-bridge.ts
26172
+ var KernelChildProcess = class {
26173
+ pid;
26174
+ command;
26175
+ args;
26176
+ parentPid;
26177
+ state = "running";
26178
+ exitCode;
26179
+ stdout = "";
26180
+ stderr = "";
26181
+ listeners = /* @__PURE__ */ new Map();
26182
+ kernel;
26183
+ cred;
26184
+ cwd;
26185
+ env;
26186
+ stdin = new Pipe();
26187
+ process;
26188
+ started = false;
26189
+ cancelled = false;
26190
+ constructor(kernel, cred, config2, pid) {
26191
+ this.stdin.interactive = true;
26192
+ if (config2.inheritStdio) this.stdin.isTTY = true;
26193
+ if (config2.stdinIgnored) this.stdin.end();
26194
+ this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
26195
+ this.kernel = kernel;
26196
+ this.cred = cred;
26197
+ this.pid = pid;
26198
+ this.command = config2.command;
26199
+ this.args = config2.args?.slice() ?? [];
26200
+ this.parentPid = config2.parentPid;
26201
+ this.cwd = config2.cwd ?? "/";
26202
+ this.env = { ...config2.env };
26203
+ }
26204
+ on(event, listener) {
26205
+ let listeners = this.listeners.get(event);
26206
+ if (!listeners) {
26207
+ listeners = /* @__PURE__ */ new Set();
26208
+ this.listeners.set(event, listeners);
26209
+ }
26210
+ listeners.add(listener);
26211
+ return this;
25861
26212
  }
25862
- return false;
25863
- }
25864
- function containsImportMeta(node2) {
25865
- if (Array.isArray(node2)) return node2.some(containsImportMeta);
25866
- if (!isNode4(node2)) return false;
25867
- if (node2.type === "MetaProperty") return true;
25868
- for (const [key, value] of Object.entries(node2)) {
25869
- if (key === "type" || key === "start" || key === "end") continue;
25870
- if (containsImportMeta(value)) return true;
26213
+ emit(event, ...args) {
26214
+ for (const listener of this.listeners.get(event) ?? []) listener(...args);
25871
26215
  }
25872
- return false;
25873
- }
25874
- function hasTopLevelAwait(node2) {
25875
- if (Array.isArray(node2)) return node2.some(hasTopLevelAwait);
25876
- if (!isNode4(node2)) return false;
25877
- if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
25878
- return false;
26216
+ exec() {
26217
+ if (this.started) return;
26218
+ this.started = true;
26219
+ queueMicrotask(() => {
26220
+ if (this.cancelled) {
26221
+ this.finish(143);
26222
+ return;
26223
+ }
26224
+ const stdout = new ChildOutput(this, "stdout");
26225
+ const stderr = new ChildOutput(this, "stderr");
26226
+ try {
26227
+ this.process = this.kernel.spawn([this.command, ...this.args], {
26228
+ cwd: this.cwd,
26229
+ env: this.env,
26230
+ cred: this.cred,
26231
+ stdin: this.stdin,
26232
+ stdout,
26233
+ stderr,
26234
+ ppid: 1
26235
+ });
26236
+ void this.process.wait().then((code) => {
26237
+ stdout.end();
26238
+ stderr.end();
26239
+ this.finish(code);
26240
+ });
26241
+ } catch (error) {
26242
+ stderr.write(error instanceof Error ? error.message : String(error));
26243
+ stderr.end();
26244
+ stdout.end();
26245
+ this.finish(1);
26246
+ }
26247
+ });
25879
26248
  }
25880
- if (node2.type === "AwaitExpression") return true;
25881
- if (node2.type === "ForOfStatement" && node2.await === true) return true;
25882
- for (const [key, value] of Object.entries(node2)) {
25883
- if (key === "type" || key === "start" || key === "end") continue;
25884
- if (hasTopLevelAwait(value)) return true;
26249
+ sendStdin(data) {
26250
+ try {
26251
+ this.stdin.write(data);
26252
+ } catch {
26253
+ }
25885
26254
  }
25886
- return false;
25887
- }
25888
- function declaredNames(declaration) {
25889
- const names = /* @__PURE__ */ new Set();
25890
- if (declaration.type === "VariableDeclaration") {
25891
- for (const declarator of declaration.declarations) {
25892
- collectPattern2(declarator.id, names);
26255
+ /** Signal EOF, so a child reading stdin to the end can finish. */
26256
+ endStdin() {
26257
+ try {
26258
+ this.stdin.end();
26259
+ } catch {
25893
26260
  }
25894
- } else if (isNode4(declaration.id)) {
25895
- names.add(declaration.id.name);
25896
26261
  }
25897
- return [...names];
25898
- }
25899
- function nameOf(node2) {
25900
- return node2.type === "Identifier" ? node2.name : node2.value;
25901
- }
25902
- function hoistedNames2(body) {
25903
- const names = blockNames2(body);
25904
- collectVars2(body, names);
25905
- return names;
25906
- }
25907
- function blockNames2(body) {
25908
- const names = /* @__PURE__ */ new Set();
25909
- for (const node2 of body ?? []) {
25910
- if (node2.type === "VariableDeclaration" && node2.kind !== "var") {
25911
- for (const declarator of node2.declarations) {
25912
- collectPattern2(declarator.id, names);
25913
- }
25914
- } else if ((node2.type === "ClassDeclaration" || node2.type === "FunctionDeclaration") && isNode4(node2.id)) {
25915
- names.add(node2.id.name);
26262
+ kill(signal = "SIGTERM") {
26263
+ if (this.process) {
26264
+ this.process.deliver(signal);
26265
+ return;
25916
26266
  }
26267
+ this.cancelled = true;
25917
26268
  }
25918
- return names;
25919
- }
25920
- function collectVars2(nodes, names) {
25921
- if (Array.isArray(nodes)) {
25922
- for (const item of nodes) collectVars2(item, names);
25923
- return;
26269
+ finish(code) {
26270
+ if (this.state === "exited") return;
26271
+ this.state = "exited";
26272
+ this.exitCode = code;
26273
+ this.emit("exit", code);
25924
26274
  }
25925
- if (!isNode4(nodes)) return;
25926
- const node2 = nodes;
25927
- if (node2.type === "FunctionDeclaration" || node2.type === "FunctionExpression" || node2.type === "ArrowFunctionExpression") {
25928
- if (isNode4(node2.id)) names.add(node2.id.name);
25929
- return;
26275
+ append(stream, text2) {
26276
+ this[stream] += text2;
26277
+ this.emit(stream, text2);
25930
26278
  }
25931
- if (node2.type === "VariableDeclaration" && node2.kind === "var") {
25932
- for (const declarator of node2.declarations) {
25933
- collectPattern2(declarator.id, names);
25934
- }
26279
+ };
26280
+ var ChildOutput = class {
26281
+ constructor(child, stream) {
26282
+ this.child = child;
26283
+ this.stream = stream;
25935
26284
  }
25936
- for (const [key, value] of Object.entries(node2)) {
25937
- if (key === "type" || key === "start" || key === "end") continue;
25938
- collectVars2(value, names);
26285
+ child;
26286
+ stream;
26287
+ isTTY = false;
26288
+ closedState = false;
26289
+ decoder = new TextDecoder();
26290
+ get closed() {
26291
+ return this.closedState;
26292
+ }
26293
+ write(data) {
26294
+ if (this.closedState) return;
26295
+ const text2 = typeof data === "string" ? data : this.decoder.decode(data, { stream: true });
26296
+ if (text2) this.child.append(this.stream, text2);
26297
+ }
26298
+ end() {
26299
+ if (this.closedState) return;
26300
+ this.closedState = true;
26301
+ const tail2 = this.decoder.decode();
26302
+ if (tail2) this.child.append(this.stream, tail2);
25939
26303
  }
26304
+ };
26305
+ var nextBridgePid = 1073741824;
26306
+ function installNodeChildProcessBridge(pod, kernel, cred) {
26307
+ const manager = pod.processManager;
26308
+ const originalSpawn = manager.spawn;
26309
+ manager.spawn = (config2) => {
26310
+ const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
26311
+ if (resolved?.kind === "builtin") {
26312
+ return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
26313
+ }
26314
+ return originalSpawn.call(manager, config2);
26315
+ };
26316
+ return () => {
26317
+ if (manager.spawn === originalSpawn) return;
26318
+ manager.spawn = originalSpawn;
26319
+ };
25940
26320
  }
25941
- function collectPattern2(node2, names) {
25942
- if (!isNode4(node2)) return;
25943
- switch (node2.type) {
25944
- case "Identifier":
25945
- names.add(node2.name);
25946
- return;
25947
- case "ObjectPattern":
25948
- for (const property of node2.properties) {
25949
- collectPattern2(property.value ?? property.argument, names);
26321
+
26322
+ // src/node/host-module-tracker.ts
26323
+ var HostModuleTracker = class {
26324
+ active = 0;
26325
+ disposed = false;
26326
+ dispose() {
26327
+ this.disposed = true;
26328
+ }
26329
+ wrapped = /* @__PURE__ */ new WeakMap();
26330
+ originals = /* @__PURE__ */ new WeakMap();
26331
+ pending = () => this.active;
26332
+ unwrap = (value) => value && (typeof value === "object" || typeof value === "function") ? this.originals.get(value) ?? value : value;
26333
+ wrap(value) {
26334
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
26335
+ const object = value;
26336
+ if (Array.isArray(value) || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) return value;
26337
+ if (this.wrapped.has(object)) return this.wrapped.get(object);
26338
+ const tracker = this;
26339
+ const proxy = new Proxy(object, {
26340
+ // CommonJS native loaders re-export by assigning exports back onto the
26341
+ // binding object. Never store a process-owned proxy in a shared module.
26342
+ set(target, key, member) {
26343
+ return Reflect.set(target, key, tracker.unwrap(member), target);
26344
+ },
26345
+ defineProperty(target, key, descriptor) {
26346
+ return Reflect.defineProperty(target, key, "value" in descriptor ? { ...descriptor, value: tracker.unwrap(descriptor.value) } : descriptor);
26347
+ },
26348
+ get(target, key) {
26349
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, key);
26350
+ const member = Reflect.get(target, key, target);
26351
+ if (descriptor && !descriptor.configurable && "value" in descriptor && !descriptor.writable) return member;
26352
+ return typeof member === "function" ? tracker.wrap(member) : member;
26353
+ },
26354
+ apply(target, receiver, args) {
26355
+ const result = Reflect.apply(target, tracker.unwrap(receiver), args.map(tracker.unwrap));
26356
+ if (result && typeof result.then === "function") {
26357
+ tracker.active++;
26358
+ return Promise.resolve(result).then(
26359
+ (value2) => {
26360
+ tracker.active--;
26361
+ return tracker.disposed ? new Promise(() => {
26362
+ }) : tracker.wrap(value2);
26363
+ },
26364
+ (error) => {
26365
+ tracker.active--;
26366
+ if (tracker.disposed) return new Promise(() => {
26367
+ });
26368
+ throw error;
26369
+ }
26370
+ );
26371
+ }
26372
+ return tracker.wrap(result);
26373
+ },
26374
+ construct(target, args, newTarget) {
26375
+ return tracker.wrap(Reflect.construct(target, args.map(tracker.unwrap), tracker.unwrap(newTarget)));
25950
26376
  }
25951
- return;
25952
- case "ArrayPattern":
25953
- for (const element of node2.elements) collectPattern2(element, names);
25954
- return;
25955
- case "AssignmentPattern":
25956
- collectPattern2(node2.left, names);
25957
- return;
25958
- case "RestElement":
25959
- collectPattern2(node2.argument, names);
25960
- return;
25961
- default:
25962
- return;
26377
+ });
26378
+ this.wrapped.set(object, proxy);
26379
+ this.originals.set(proxy, object);
26380
+ return proxy;
25963
26381
  }
25964
- }
25965
- function applyEdits(source, edits) {
25966
- const ordered = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
25967
- let out = source;
25968
- for (const edit of ordered) out = out.slice(0, edit.start) + edit.text + out.slice(edit.end);
25969
- return out;
25970
- }
25971
- function isNode4(value) {
25972
- return typeof value === "object" && value !== null && typeof value.type === "string";
25973
- }
25974
- var NOTHING2 = [];
25975
- var PROPERTY2 = ["property", "key"];
25976
- var LABEL2 = ["label"];
26382
+ };
26383
+
26384
+ // src/node/commonjs-engine.ts
26385
+ init_path();
25977
26386
  var EXTENSIONS = [".js", ".mjs", ".cjs", ".json", ".node"];
26387
+ var PREFIX_ONLY_BUILTINS = /* @__PURE__ */ new Set(["test", "test/reporters", "sea", "sqlite"]);
25978
26388
  var CONDITION_SETS = {
25979
26389
  import: [["node", "import", "module", "default"], ["node", "require", "default"], ["default"]],
25980
26390
  require: [["node", "require", "default"], ["node", "import", "module", "default"], ["default"]]
@@ -26314,7 +26724,8 @@ ${code}
26314
26724
  }
26315
26725
  builtin(specifier, importer) {
26316
26726
  const name = specifier.replace(/^node:/, "");
26317
- if (!Object.prototype.hasOwnProperty.call(this.builtins, name)) {
26727
+ const prefixOnly = PREFIX_ONLY_BUILTINS.has(name) && !specifier.startsWith("node:");
26728
+ if (prefixOnly || !Object.prototype.hasOwnProperty.call(this.builtins, name)) {
26318
26729
  return Object.prototype.hasOwnProperty.call(this.overrides, specifier) ? { found: true, value: this.overrides[specifier] } : { found: false, value: void 0 };
26319
26730
  }
26320
26731
  if (name === "module" && importer) {
@@ -27584,21 +27995,23 @@ function resolveTarget(input, overrides, defaultProtocol) {
27584
27995
  };
27585
27996
  }
27586
27997
  function isLoopback(hostname) {
27587
- return hostname === "localhost" || hostname === "0.0.0.0" || hostname === "::1" || hostname === "::" || hostname.startsWith("127.") || hostname.endsWith(".localhost");
27998
+ return isLoopbackHostname(hostname);
27588
27999
  }
27589
28000
  var VirtualClientRequest = class extends streamModule4.Writable {
27590
- constructor(target, router, fetchImpl, trackRequest) {
28001
+ constructor(target, router, fetchImpl, trackRequest, loopback = void 0) {
27591
28002
  super();
27592
28003
  this.target = target;
27593
28004
  this.router = router;
27594
28005
  this.fetchImpl = fetchImpl;
27595
28006
  this.trackRequest = trackRequest;
28007
+ this.loopback = loopback;
27596
28008
  for (const [name, value] of Object.entries(target.headers)) this.setHeader(name, value);
27597
28009
  }
27598
28010
  target;
27599
28011
  router;
27600
28012
  fetchImpl;
27601
28013
  trackRequest;
28014
+ loopback;
27602
28015
  socket = socketStub();
27603
28016
  connection = this.socket;
27604
28017
  chunks = [];
@@ -27712,7 +28125,9 @@ var VirtualClientRequest = class extends streamModule4.Writable {
27712
28125
  } catch (error) {
27713
28126
  clearTimeout(this.timer);
27714
28127
  if (this.destroyedByUser) return;
27715
- const failure2 = error instanceof Error ? error : new Error(String(error));
28128
+ const thrown = error instanceof Error ? error : new Error(String(error));
28129
+ const cause = thrown.cause;
28130
+ const failure2 = cause instanceof Error && "code" in cause ? cause : thrown;
27716
28131
  if (!("code" in failure2)) Object.assign(failure2, { code: "ECONNREFUSED" });
27717
28132
  this.emit("error", failure2);
27718
28133
  } finally {
@@ -27720,18 +28135,21 @@ var VirtualClientRequest = class extends streamModule4.Writable {
27720
28135
  }
27721
28136
  }
27722
28137
  async viaRouter(body) {
27723
- if (!this.router.activePortsIncludes(this.target.port)) {
28138
+ const init = {
28139
+ method: this.target.method,
28140
+ path: this.target.path,
28141
+ headers: this.getHeaders(),
28142
+ body: body.length ? new Uint8Array(body) : null
28143
+ };
28144
+ const local = this.router.activePortsIncludes(this.target.port);
28145
+ const remote = local || !this.loopback ? null : await this.loopback(this.target.port, init);
28146
+ if (!local && !remote) {
27724
28147
  throw Object.assign(
27725
28148
  new Error(`connect ECONNREFUSED ${this.target.hostname}:${this.target.port}`),
27726
28149
  { code: "ECONNREFUSED", errno: -61, syscall: "connect", address: this.target.hostname, port: this.target.port }
27727
28150
  );
27728
28151
  }
27729
- const result = await this.router.request(this.target.port, {
27730
- method: this.target.method,
27731
- path: this.target.path,
27732
- headers: this.getHeaders(),
27733
- body: body.length ? new Uint8Array(body) : null
27734
- });
28152
+ const result = remote ?? await this.router.request(this.target.port, init);
27735
28153
  const body_ = result.body;
27736
28154
  const bytes2 = typeof body_ === "string" ? new TextEncoder().encode(body_) : body_ instanceof ArrayBuffer ? new Uint8Array(body_) : body_ ?? new Uint8Array();
27737
28155
  const status = result.statusCode ?? 200;
@@ -27771,7 +28189,13 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
27771
28189
  const request = (input, second, third) => {
27772
28190
  const overrides = second && typeof second === "object" && !(second instanceof Function) ? second : void 0;
27773
28191
  const callback = [second, third].find((value) => typeof value === "function");
27774
- const req = new VirtualClientRequest(resolveTarget(input, overrides, defaultProtocol), router, options.fetch, options.trackRequest);
28192
+ const req = new VirtualClientRequest(
28193
+ resolveTarget(input, overrides, defaultProtocol),
28194
+ router,
28195
+ options.fetch,
28196
+ options.trackRequest,
28197
+ options.loopback
28198
+ );
27775
28199
  if (callback) req.on("response", callback);
27776
28200
  return req;
27777
28201
  };
@@ -27798,6 +28222,62 @@ function createHttpModule(router, owner, options = {}, defaultProtocol = "http:"
27798
28222
  }
27799
28223
  };
27800
28224
  }
28225
+ function createVirtualFetch(router, options = {}) {
28226
+ return (async (input, init) => {
28227
+ const request = new Request(input, init);
28228
+ const url = new URL(request.url);
28229
+ const release = options.trackRequest?.();
28230
+ try {
28231
+ if (!isLoopback(url.hostname)) {
28232
+ if (!options.fetch) {
28233
+ throw Object.assign(new TypeError("fetch failed"), {
28234
+ cause: Object.assign(new Error(`getaddrinfo ENOTFOUND ${url.hostname}`), { code: "ENOTFOUND" })
28235
+ });
28236
+ }
28237
+ return await options.fetch(request);
28238
+ }
28239
+ const port = Number(url.port || (url.protocol === "https:" ? 443 : 80));
28240
+ const headers = {};
28241
+ request.headers.forEach((value, key) => {
28242
+ headers[key] = value;
28243
+ });
28244
+ const bodyless = request.method === "GET" || request.method === "HEAD";
28245
+ const loopbackRequest = {
28246
+ method: request.method,
28247
+ path: `${url.pathname}${url.search}`,
28248
+ headers,
28249
+ body: bodyless ? null : new Uint8Array(await request.arrayBuffer())
28250
+ };
28251
+ const result = router.activePortsIncludes(port) ? await router.request(port, loopbackRequest) : await options.loopback?.(port, loopbackRequest) ?? null;
28252
+ if (!result) {
28253
+ throw Object.assign(new TypeError("fetch failed"), {
28254
+ cause: Object.assign(new Error(`connect ECONNREFUSED ${url.hostname}:${port}`), {
28255
+ code: "ECONNREFUSED",
28256
+ errno: -61,
28257
+ syscall: "connect",
28258
+ address: url.hostname,
28259
+ port
28260
+ })
28261
+ });
28262
+ }
28263
+ const status = result.statusCode ?? 200;
28264
+ const responseHeaders = new Headers();
28265
+ for (const [name, value] of Object.entries(result.headers ?? {})) {
28266
+ for (const part of Array.isArray(value) ? value : [value]) responseHeaders.append(name, String(part));
28267
+ }
28268
+ const raw = result.body;
28269
+ const bytes2 = typeof raw === "string" ? new TextEncoder().encode(raw) : raw instanceof ArrayBuffer ? new Uint8Array(raw) : raw ?? new Uint8Array();
28270
+ const nullBody = status === 204 || status === 205 || status === 304 || request.method === "HEAD";
28271
+ return new Response(nullBody ? null : new Uint8Array(bytes2), {
28272
+ status,
28273
+ statusText: result.statusMessage ?? STATUS_CODES[status] ?? "",
28274
+ headers: responseHeaders
28275
+ });
28276
+ } finally {
28277
+ release?.();
28278
+ }
28279
+ });
28280
+ }
27801
28281
  function validateHeaderName(name) {
27802
28282
  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" });
27803
28283
  }
@@ -27817,6 +28297,492 @@ function socketStub() {
27817
28297
  }
27818
28298
  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"];
27819
28299
  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" };
28300
+
28301
+ // src/node/test-module.ts
28302
+ function parseArguments(args) {
28303
+ let name = "<anonymous>";
28304
+ let options = {};
28305
+ let fn;
28306
+ for (const arg of args) {
28307
+ if (typeof arg === "string") name = arg;
28308
+ else if (typeof arg === "function") {
28309
+ fn = arg;
28310
+ if (name === "<anonymous>" && arg.name) name = arg.name;
28311
+ } else if (arg && typeof arg === "object") options = arg;
28312
+ }
28313
+ return { name, options, fn };
28314
+ }
28315
+ function errorText(error) {
28316
+ if (error instanceof Error) {
28317
+ const stack = error.stack ?? `${error.name}: ${error.message}`;
28318
+ return stack.includes(error.message) ? stack : `${error.name}: ${error.message}
28319
+ ${stack}`;
28320
+ }
28321
+ return String(error);
28322
+ }
28323
+ function indent(text2, spaces) {
28324
+ const pad2 = " ".repeat(spaces);
28325
+ return text2.split("\n").map((line) => line ? pad2 + line : line).join("\n");
28326
+ }
28327
+ function duration(started) {
28328
+ return (performance.now() - started).toFixed(6).replace(/\.?0+$/, "");
28329
+ }
28330
+ function invoke(host2, fn, context, timeout) {
28331
+ const body = new Promise((resolve3, reject) => {
28332
+ if (fn.length >= 2) {
28333
+ let settled = false;
28334
+ const done = (error) => {
28335
+ if (settled) return;
28336
+ settled = true;
28337
+ if (error) reject(error);
28338
+ else resolve3();
28339
+ };
28340
+ try {
28341
+ const returned = fn(context, done);
28342
+ if (returned && typeof returned.then === "function") {
28343
+ reject(new Error("passed a callback but also returned a Promise"));
28344
+ }
28345
+ } catch (error) {
28346
+ done(error);
28347
+ }
28348
+ return;
28349
+ }
28350
+ try {
28351
+ Promise.resolve(fn(context)).then(() => resolve3(), reject);
28352
+ } catch (error) {
28353
+ reject(error);
28354
+ }
28355
+ });
28356
+ if (timeout === void 0 || !Number.isFinite(timeout)) return body;
28357
+ return new Promise((resolve3, reject) => {
28358
+ const timer = host2.setTimeout(() => reject(new Error(`test timed out after ${timeout}ms`)), timeout);
28359
+ body.then(
28360
+ () => {
28361
+ host2.clearTimeout(timer);
28362
+ resolve3();
28363
+ },
28364
+ (error) => {
28365
+ host2.clearTimeout(timer);
28366
+ reject(error);
28367
+ }
28368
+ );
28369
+ });
28370
+ }
28371
+ function createMockTracker() {
28372
+ const restorers = [];
28373
+ const fn = (original = () => void 0, implementation = original) => {
28374
+ let current = implementation;
28375
+ const once = /* @__PURE__ */ new Map();
28376
+ const calls = [];
28377
+ const mocked = function(...args) {
28378
+ const index = calls.length;
28379
+ const chosen = once.get(index) ?? current;
28380
+ once.delete(index);
28381
+ const call = { arguments: args, result: void 0, error: void 0, this: this };
28382
+ calls.push(call);
28383
+ try {
28384
+ call.result = chosen.apply(this, args);
28385
+ return call.result;
28386
+ } catch (error) {
28387
+ call.error = error;
28388
+ throw error;
28389
+ }
28390
+ };
28391
+ Object.defineProperty(mocked, "mock", {
28392
+ value: {
28393
+ calls,
28394
+ callCount: () => calls.length,
28395
+ resetCalls: () => {
28396
+ calls.length = 0;
28397
+ },
28398
+ mockImplementation: (next) => {
28399
+ current = next;
28400
+ },
28401
+ mockImplementationOnce: (next, onCall) => {
28402
+ once.set(onCall ?? calls.length, next);
28403
+ },
28404
+ restore: () => {
28405
+ current = original;
28406
+ }
28407
+ }
28408
+ });
28409
+ return mocked;
28410
+ };
28411
+ const method = (object, name, implementation) => {
28412
+ const original = object[name];
28413
+ if (typeof original !== "function") {
28414
+ throw new TypeError(`The property '${name}' is not a function and cannot be mocked`);
28415
+ }
28416
+ const mocked = fn(original, implementation ?? original);
28417
+ object[name] = mocked;
28418
+ const restore = () => {
28419
+ object[name] = original;
28420
+ };
28421
+ mocked.mock.restore = restore;
28422
+ restorers.push(restore);
28423
+ return mocked;
28424
+ };
28425
+ return {
28426
+ fn,
28427
+ method,
28428
+ reset: () => {
28429
+ for (const restore of restorers.splice(0)) restore();
28430
+ },
28431
+ restoreAll: () => {
28432
+ for (const restore of restorers.splice(0)) restore();
28433
+ }
28434
+ };
28435
+ }
28436
+ function createTestModule(host2) {
28437
+ const root = {
28438
+ kind: "suite",
28439
+ name: "",
28440
+ options: {},
28441
+ parent: null,
28442
+ children: [],
28443
+ hooks: { before: [], after: [], beforeEach: [], afterEach: [] }
28444
+ };
28445
+ let collecting = root;
28446
+ let scheduled = false;
28447
+ let running = null;
28448
+ const mock = createMockTracker();
28449
+ const counts = { tests: 0, suites: 0, pass: 0, fail: 0, cancelled: 0, skipped: 0, todo: 0 };
28450
+ const failures = [];
28451
+ let tapIndex = 0;
28452
+ let onlyWarned = false;
28453
+ const settings = () => host2.settings();
28454
+ const tap = () => settings().reporter === "tap";
28455
+ const schedule = () => {
28456
+ if (scheduled) return;
28457
+ scheduled = true;
28458
+ host2.setTimeout(() => {
28459
+ running = runAll();
28460
+ }, 0);
28461
+ };
28462
+ const fullName = (node2) => {
28463
+ const parts = [];
28464
+ let cursor = node2;
28465
+ while (cursor && cursor !== root) {
28466
+ parts.unshift(cursor.name);
28467
+ cursor = cursor.parent;
28468
+ }
28469
+ return parts.join(" ");
28470
+ };
28471
+ const matchesPattern2 = (name) => {
28472
+ const patterns = settings().namePatterns ?? [];
28473
+ if (patterns.length === 0) return true;
28474
+ return patterns.some((pattern) => {
28475
+ try {
28476
+ return new RegExp(pattern).test(name);
28477
+ } catch {
28478
+ return name.includes(pattern);
28479
+ }
28480
+ });
28481
+ };
28482
+ const hasOnly = (node2) => Boolean(node2.options.only) || node2.kind === "suite" && node2.children.some(hasOnly);
28483
+ const report = (depth, outcome, name, started, detail) => {
28484
+ if (tap()) {
28485
+ tapIndex += 1;
28486
+ const ok2 = outcome === "fail" ? "not ok" : "ok";
28487
+ const directive = outcome === "skip" ? " # SKIP" : outcome === "todo" ? " # TODO" : outcome === "cancelled" ? " # CANCELLED" : "";
28488
+ host2.write(`${ok2} ${tapIndex} - ${name}${directive}
28489
+ `);
28490
+ host2.write(` ---
28491
+ duration_ms: ${duration(started)}
28492
+ `);
28493
+ if (detail) host2.write(` error: |-
28494
+ ${indent(detail, 4)}
28495
+ `);
28496
+ host2.write(" ...\n");
28497
+ return;
28498
+ }
28499
+ const pad2 = " ".repeat(depth);
28500
+ const mark = outcome === "pass" ? "\u2714" : outcome === "fail" ? "\u2716" : outcome === "cancelled" ? "\u2716" : "\uFE63";
28501
+ const suffix = outcome === "skip" ? " # SKIP" : outcome === "todo" ? " # TODO" : "";
28502
+ host2.write(`${pad2}${mark} ${name} (${duration(started)}ms)${suffix}
28503
+ `);
28504
+ if (detail) host2.write(`${indent(detail, depth * 2 + 2)}
28505
+ `);
28506
+ };
28507
+ const record = (outcome, name, error) => {
28508
+ counts.tests += 1;
28509
+ if (outcome === "pass") counts.pass += 1;
28510
+ else if (outcome === "fail") {
28511
+ counts.fail += 1;
28512
+ failures.push({ name, error });
28513
+ } else if (outcome === "cancelled") {
28514
+ counts.cancelled += 1;
28515
+ failures.push({ name, error });
28516
+ } else if (outcome === "skip") counts.skipped += 1;
28517
+ else counts.todo += 1;
28518
+ };
28519
+ const ancestors = (suite) => {
28520
+ const chain = [];
28521
+ for (let cursor = suite; cursor; cursor = cursor.parent) chain.unshift(cursor);
28522
+ return chain;
28523
+ };
28524
+ const runTest = async (name, display, options, fn, depth, suite, extraHooks, runOnly) => {
28525
+ const started = performance.now();
28526
+ if (!matchesPattern2(display)) return true;
28527
+ if (runOnly && !options.only) return true;
28528
+ if (options.skip) {
28529
+ record("skip", display);
28530
+ report(depth, "skip", name, started);
28531
+ return true;
28532
+ }
28533
+ if (!fn) {
28534
+ const outcome = options.todo ? "todo" : "skip";
28535
+ record(outcome, display);
28536
+ report(depth, outcome, name, started);
28537
+ return true;
28538
+ }
28539
+ const controller = new AbortController();
28540
+ const subtests = [];
28541
+ const ownHooks = { before: [], after: [], beforeEach: [], afterEach: [] };
28542
+ let skipped = false;
28543
+ let todo = options.todo ?? false;
28544
+ const context = {
28545
+ name,
28546
+ fullName: display,
28547
+ signal: controller.signal,
28548
+ mock,
28549
+ test: (...args) => {
28550
+ const sub = parseArguments(args);
28551
+ const promise = runTest(
28552
+ sub.name,
28553
+ `${display} ${sub.name}`,
28554
+ sub.options,
28555
+ sub.fn,
28556
+ depth + 1,
28557
+ suite,
28558
+ ownHooks,
28559
+ false
28560
+ );
28561
+ subtests.push(promise);
28562
+ return promise.then(() => void 0);
28563
+ },
28564
+ skip: (message) => {
28565
+ skipped = message ?? true;
28566
+ },
28567
+ todo: (message) => {
28568
+ todo = message ?? true;
28569
+ },
28570
+ diagnostic: (message) => {
28571
+ host2.write(tap() ? `# ${message}
28572
+ ` : `${" ".repeat(depth + 1)}\u2139 ${message}
28573
+ `);
28574
+ },
28575
+ plan: () => {
28576
+ },
28577
+ before: (hook2) => ownHooks.before.push(hook2),
28578
+ after: (hook2) => ownHooks.after.push(hook2),
28579
+ beforeEach: (hook2) => ownHooks.beforeEach.push(hook2),
28580
+ afterEach: (hook2) => ownHooks.afterEach.push(hook2)
28581
+ };
28582
+ let error;
28583
+ try {
28584
+ const chain = ancestors(suite);
28585
+ for (const level of chain) for (const hook2 of level.hooks.beforeEach) await invoke(host2, hook2, context, void 0);
28586
+ for (const hook2 of extraHooks.beforeEach) await invoke(host2, hook2, context, void 0);
28587
+ await invoke(host2, fn, context, options.timeout);
28588
+ const results = await Promise.all(subtests);
28589
+ for (const hook2 of ownHooks.after) await invoke(host2, hook2, context, void 0);
28590
+ if (results.includes(false)) error = new Error(`${results.filter((ok2) => !ok2).length} subtest(s) failed`);
28591
+ for (const hook2 of extraHooks.afterEach) await invoke(host2, hook2, context, void 0);
28592
+ for (const level of [...chain].reverse()) for (const hook2 of level.hooks.afterEach) await invoke(host2, hook2, context, void 0);
28593
+ } catch (caught) {
28594
+ error = caught;
28595
+ } finally {
28596
+ controller.abort();
28597
+ }
28598
+ if (skipped) {
28599
+ record("skip", display);
28600
+ report(depth, "skip", name, started);
28601
+ return true;
28602
+ }
28603
+ if (error !== void 0) {
28604
+ if (todo) {
28605
+ record("todo", display);
28606
+ report(depth, "todo", name, started);
28607
+ return true;
28608
+ }
28609
+ record("fail", display, error);
28610
+ report(depth, "fail", name, started, errorText(error));
28611
+ return false;
28612
+ }
28613
+ record(todo ? "todo" : "pass", display);
28614
+ report(depth, todo ? "todo" : "pass", name, started);
28615
+ return true;
28616
+ };
28617
+ const runSuite = async (suite, depth, runOnly) => {
28618
+ const started = performance.now();
28619
+ const display = fullName(suite);
28620
+ const isRoot = suite === root;
28621
+ if (!isRoot) {
28622
+ counts.suites += 1;
28623
+ if (tap()) host2.write(`# Subtest: ${suite.name}
28624
+ `);
28625
+ else host2.write(`${" ".repeat(depth)}\u25B6 ${suite.name}
28626
+ `);
28627
+ if (suite.options.skip) {
28628
+ for (const child of suite.children) if (child.kind === "test") record("skip", fullName(child));
28629
+ report(depth, "skip", suite.name, started);
28630
+ return true;
28631
+ }
28632
+ }
28633
+ const context = { name: suite.name, signal: new AbortController().signal };
28634
+ let ok2 = true;
28635
+ try {
28636
+ for (const hook2 of suite.hooks.before) await invoke(host2, hook2, context, void 0);
28637
+ } catch (error) {
28638
+ for (const child of suite.children) {
28639
+ if (child.kind === "test") {
28640
+ record("cancelled", fullName(child), error);
28641
+ report(depth + (isRoot ? 0 : 1), "cancelled", child.name, started, errorText(error));
28642
+ }
28643
+ }
28644
+ if (!isRoot) report(depth, "fail", suite.name, started);
28645
+ return false;
28646
+ }
28647
+ const only = runOnly || settings().only === true && suite.children.some(hasOnly);
28648
+ for (const child of suite.children) {
28649
+ const childDepth = depth + (isRoot ? 0 : 1);
28650
+ if (child.kind === "suite") {
28651
+ if (only && !hasOnly(child) && !child.options.only) continue;
28652
+ ok2 = await runSuite(child, childDepth, only && !child.options.only) && ok2;
28653
+ } else {
28654
+ ok2 = await runTest(
28655
+ child.name,
28656
+ fullName(child),
28657
+ child.options,
28658
+ child.fn,
28659
+ childDepth,
28660
+ suite,
28661
+ { before: [], after: [], beforeEach: [], afterEach: [] },
28662
+ only
28663
+ ) && ok2;
28664
+ }
28665
+ }
28666
+ try {
28667
+ for (const hook2 of suite.hooks.after) await invoke(host2, hook2, context, void 0);
28668
+ } catch (error) {
28669
+ ok2 = false;
28670
+ failures.push({ name: `${display || "<root>"} (after hook)`, error });
28671
+ host2.write(`${indent(`\u2716 after hook failed
28672
+ ${errorText(error)}`, depth * 2)}
28673
+ `);
28674
+ }
28675
+ if (!isRoot) {
28676
+ if (tap()) host2.write(`${ok2 ? "ok" : "not ok"} ${++tapIndex} - ${suite.name}
28677
+ `);
28678
+ else host2.write(`${" ".repeat(depth)}${ok2 ? "\u25B6" : "\u2716"} ${suite.name} (${duration(started)}ms)
28679
+ `);
28680
+ }
28681
+ return ok2;
28682
+ };
28683
+ const runAll = async () => {
28684
+ const release = host2.keepAlive();
28685
+ const started = performance.now();
28686
+ if (tap()) host2.write("TAP version 13\n");
28687
+ if (!settings().only && !onlyWarned && root.children.some(hasOnly)) {
28688
+ onlyWarned = true;
28689
+ host2.write("\u2139 'only' and 'runOnly' require the --test-only command-line option.\n");
28690
+ }
28691
+ let ok2 = true;
28692
+ try {
28693
+ ok2 = await runSuite(root, 0, false);
28694
+ } catch (error) {
28695
+ ok2 = false;
28696
+ failures.push({ name: "<test runner>", error });
28697
+ }
28698
+ mock.restoreAll();
28699
+ if (tap()) {
28700
+ host2.write(`1..${tapIndex}
28701
+ `);
28702
+ for (const [key, value] of Object.entries(counts)) host2.write(`# ${key} ${value}
28703
+ `);
28704
+ host2.write(`# duration_ms ${duration(started)}
28705
+ `);
28706
+ } else {
28707
+ for (const [key, value] of Object.entries(counts)) host2.write(`\u2139 ${key} ${value}
28708
+ `);
28709
+ host2.write(`\u2139 duration_ms ${duration(started)}
28710
+ `);
28711
+ if (failures.length > 0) {
28712
+ host2.write("\n\u2716 failing tests:\n\n");
28713
+ for (const failure2 of failures) host2.write(`test at ${failure2.name}
28714
+ ${indent(errorText(failure2.error), 2)}
28715
+
28716
+ `);
28717
+ }
28718
+ }
28719
+ release();
28720
+ host2.exit(ok2 && counts.fail === 0 && counts.cancelled === 0 ? 0 : 1);
28721
+ };
28722
+ const declareTest = (extra) => (...args) => {
28723
+ const { name, options, fn } = parseArguments(args);
28724
+ if (running && collecting === root && scheduled) {
28725
+ host2.write(`\u2716 test "${name}" was declared after the run started and did not run
28726
+ `);
28727
+ return Promise.resolve();
28728
+ }
28729
+ collecting.children.push({ kind: "test", name, options: { ...options, ...extra }, parent: collecting, fn });
28730
+ schedule();
28731
+ return Promise.resolve();
28732
+ };
28733
+ const declareSuite = (extra) => (...args) => {
28734
+ const { name, options, fn } = parseArguments(args);
28735
+ const suite = {
28736
+ kind: "suite",
28737
+ name,
28738
+ options: { ...options, ...extra },
28739
+ parent: collecting,
28740
+ children: [],
28741
+ hooks: { before: [], after: [], beforeEach: [], afterEach: [] }
28742
+ };
28743
+ collecting.children.push(suite);
28744
+ schedule();
28745
+ const previous = collecting;
28746
+ collecting = suite;
28747
+ try {
28748
+ fn?.({ name, signal: new AbortController().signal });
28749
+ } finally {
28750
+ collecting = previous;
28751
+ }
28752
+ return Promise.resolve();
28753
+ };
28754
+ const withModifiers = (make) => {
28755
+ const base2 = make({});
28756
+ base2.skip = make({ skip: true });
28757
+ base2.todo = make({ todo: true });
28758
+ base2.only = make({ only: true });
28759
+ return base2;
28760
+ };
28761
+ const test = withModifiers(declareTest);
28762
+ const describe2 = withModifiers(declareSuite);
28763
+ const hook = (kind) => (fn) => {
28764
+ collecting.hooks[kind].push(fn);
28765
+ schedule();
28766
+ };
28767
+ Object.assign(test, {
28768
+ test,
28769
+ it: test,
28770
+ describe: describe2,
28771
+ suite: describe2,
28772
+ before: hook("before"),
28773
+ after: hook("after"),
28774
+ beforeEach: hook("beforeEach"),
28775
+ afterEach: hook("afterEach"),
28776
+ mock,
28777
+ run: () => {
28778
+ throw Object.assign(new Error("node:test run() is not available in this container; use `node --test`"), {
28779
+ code: "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM"
28780
+ });
28781
+ }
28782
+ });
28783
+ Object.defineProperty(test, "default", { value: test, enumerable: false });
28784
+ return test;
28785
+ }
27820
28786
  var hashes = { md5, sha1: sha1$1, "sha-1": sha1$1, sha224, "sha-224": sha224, sha256: sha256, "sha-256": sha256, sha384, "sha-384": sha384, sha512: sha512, "sha-512": sha512 };
27821
28787
  function createCryptoModule() {
27822
28788
  const cryptoObject = globalThis.crypto;
@@ -28773,7 +29739,7 @@ function createCoreModules(options) {
28773
29739
  });
28774
29740
  const stderrWrite = options.stderr ?? (() => {
28775
29741
  });
28776
- const timers = createTrackedTimers();
29742
+ const timers = createTrackedTimers((error) => reportUncaught(error));
28777
29743
  const processObject = Object.assign(new EventEmitter4(), processShim, {
28778
29744
  argv: options.argv?.slice() ?? ["/usr/bin/node"],
28779
29745
  argv0: "node",
@@ -28803,16 +29769,65 @@ function createCoreModules(options) {
28803
29769
  uptime: () => performance.now() / 1e3,
28804
29770
  memoryUsage: () => ({ rss: 0, heapTotal: 0, heapUsed: 0, external: 0, arrayBuffers: 0 }),
28805
29771
  cpuUsage: () => ({ user: 0, system: 0 }),
28806
- exit: (code = 0) => {
28807
- exitCode = code;
28808
- processObject.exitCode = code;
28809
- options.onExit?.(code);
29772
+ /* A bare `process.exit()` ends with `process.exitCode`, as in Node. */
29773
+ exit: (code) => {
29774
+ const status = normalizeExitCode(code ?? processObject.exitCode);
29775
+ exitCode = status;
29776
+ processObject.exitCode = status;
29777
+ emitExit(status);
29778
+ options.onExit?.(status);
29779
+ },
29780
+ nextTick: (fn, ...args) => {
29781
+ queueMicrotask(() => {
29782
+ try {
29783
+ fn(...args);
29784
+ } catch (error) {
29785
+ reportUncaught(error);
29786
+ }
29787
+ });
28810
29788
  },
28811
29789
  kill: () => true
28812
29790
  });
29791
+ for (const method of EVENT_EMITTER_METHODS) {
29792
+ const implementation = EventEmitter4.prototype[method];
29793
+ if (typeof implementation === "function") processObject[method] = implementation;
29794
+ }
28813
29795
  const tty = options.tty === true;
28814
29796
  processObject.stdout = makeOutputStream(stdoutWrite, 1, tty);
28815
29797
  processObject.stderr = makeOutputStream(stderrWrite, 2, tty);
29798
+ let exiting = false;
29799
+ const emitExit = (status) => {
29800
+ if (exiting) return;
29801
+ exiting = true;
29802
+ try {
29803
+ processObject.emit("exit", status);
29804
+ } catch {
29805
+ }
29806
+ };
29807
+ const reportUncaught = (error, origin = "uncaughtException") => {
29808
+ if (!exiting && processObject.listenerCount(origin) > 0) {
29809
+ try {
29810
+ processObject.emit(origin, error, origin === "uncaughtException" ? origin : void 0);
29811
+ return;
29812
+ } catch (handlerError) {
29813
+ error = handlerError;
29814
+ }
29815
+ }
29816
+ stderrWrite(formatUncaught(error));
29817
+ try {
29818
+ processObject.exit(1);
29819
+ } catch {
29820
+ }
29821
+ };
29822
+ const defer = (fn) => {
29823
+ queueMicrotask(() => {
29824
+ try {
29825
+ fn();
29826
+ } catch (error) {
29827
+ reportUncaught(error);
29828
+ }
29829
+ });
29830
+ };
28816
29831
  const stdin = options.interactiveStdin ? new streamModule4.PassThrough() : new streamModule4.Readable({ read() {
28817
29832
  this.push(null);
28818
29833
  } });
@@ -28832,7 +29847,7 @@ function createCoreModules(options) {
28832
29847
  });
28833
29848
  processObject.stdin = stdin;
28834
29849
  Reflect.deleteProperty(processObject, "browser");
28835
- const fs = createFsModule(volume, () => cwd, options.stdinPath);
29850
+ const fs = createFsModule(volume, () => cwd, options.stdinPath, defer);
28836
29851
  const path = createPathModule(() => cwd);
28837
29852
  const consoleObject = new Console(stdoutWrite, stderrWrite);
28838
29853
  const os = createOsModule();
@@ -28845,20 +29860,24 @@ function createCoreModules(options) {
28845
29860
  }
28846
29861
  };
28847
29862
  let inFlightRequests = 0;
29863
+ const trackRequest = () => {
29864
+ inFlightRequests++;
29865
+ return () => {
29866
+ inFlightRequests--;
29867
+ };
29868
+ };
29869
+ const ambientFetch = options.http?.fetch ?? (typeof globalThis.fetch === "function" ? globalThis.fetch.bind(globalThis) : void 0);
29870
+ const outboundFetch = options.http?.policy && ambientFetch ? policedFetch(options.http.policy, ambientFetch) : ambientFetch;
28848
29871
  const httpOptions = {
28849
- ...options.http?.fetch ? { fetch: options.http.fetch } : {},
28850
- trackRequest: () => {
28851
- inFlightRequests++;
28852
- return () => {
28853
- inFlightRequests--;
28854
- };
28855
- }
29872
+ ...outboundFetch ? { fetch: outboundFetch } : {},
29873
+ ...options.http?.loopback ? { loopback: options.http.loopback } : {},
29874
+ trackRequest
28856
29875
  };
28857
29876
  const http = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "http:") : createUnsupportedModule("http");
28858
29877
  const https = options.http ? createHttpModule(options.http.router, options.http.owner, httpOptions, "https:") : createUnsupportedModule("https");
28859
29878
  const childProcess = options.spawnChild ? createChildProcessModule(options.spawnChild, () => cwd, options.syncSpawn, () => ({ ...processObject.env })) : createUnsupportedModule("child_process");
28860
29879
  const readline = createReadlineModule(() => processObject.stdin, () => processObject.stdout);
28861
- const dns = createDnsModule();
29880
+ const dns = createDnsModule(defer);
28862
29881
  const builtins = {
28863
29882
  assert: assert_module_default,
28864
29883
  "assert/strict": assert_module_default.strict ?? assert_module_default,
@@ -28894,7 +29913,27 @@ function createCoreModules(options) {
28894
29913
  url: url_module_default,
28895
29914
  util: util_module_default,
28896
29915
  "util/types": util_module_default.types ?? {},
28897
- zlib: zlib_module_default
29916
+ zlib: zlib_module_default,
29917
+ test: createTestModule({
29918
+ write: stdoutWrite,
29919
+ setTimeout: (fn, ms) => timers.api.setTimeout(fn, ms),
29920
+ clearTimeout: (handle) => timers.api.clearTimeout(handle),
29921
+ /* A pending interval is what keeps a process alive in this runtime; a test
29922
+ * awaiting nothing but promises would otherwise be judged finished. */
29923
+ keepAlive: () => {
29924
+ const handle = timers.api.setInterval(() => {
29925
+ }, 1 << 30);
29926
+ return () => timers.api.clearInterval(handle);
29927
+ },
29928
+ exit: (code) => {
29929
+ processObject.exitCode = code;
29930
+ try {
29931
+ processObject.exit(code);
29932
+ } catch {
29933
+ }
29934
+ },
29935
+ settings: () => parseTestSettings(env2.SANDBOXEDJS_NODE_TEST)
29936
+ })
28898
29937
  };
28899
29938
  for (const name of stubNames) builtins[name] = createUnsupportedModule(name);
28900
29939
  builtins.net = createNetModule();
@@ -28902,8 +29941,26 @@ function createCoreModules(options) {
28902
29941
  Buffer: Buffer2,
28903
29942
  console: consoleObject,
28904
29943
  process: processObject,
28905
- ...timers.api
29944
+ ...timers.api,
29945
+ queueMicrotask: (fn) => queueMicrotask(() => {
29946
+ try {
29947
+ fn();
29948
+ } catch (error) {
29949
+ reportUncaught(error);
29950
+ }
29951
+ })
28906
29952
  };
29953
+ if (options.http) {
29954
+ globals.fetch = createVirtualFetch(options.http.router, {
29955
+ ...outboundFetch ? { fetch: outboundFetch } : {},
29956
+ ...options.http.loopback ? { loopback: options.http.loopback } : {},
29957
+ trackRequest
29958
+ });
29959
+ const NativeWebSocket = globalThis.WebSocket;
29960
+ if (options.http.policy && NativeWebSocket) {
29961
+ globals.WebSocket = policedWebSocket(NativeWebSocket, options.http.policy);
29962
+ }
29963
+ }
28907
29964
  const globalOverlay = new Proxy(globalThis, {
28908
29965
  get: (target, key) => typeof key === "string" && key in globals ? globals[key] : key === "global" || key === "globalThis" ? globalOverlay : Reflect.get(target, key),
28909
29966
  has: (target, key) => typeof key === "string" && key in globals || Reflect.has(target, key)
@@ -28919,6 +29976,10 @@ function createCoreModules(options) {
28919
29976
  /** Client requests sent but not yet read to completion. */
28920
29977
  pendingRequests: () => inFlightRequests,
28921
29978
  cancelTimers: timers.cancelAll,
29979
+ reportUncaught: (error) => reportUncaught(error),
29980
+ reportUnhandledRejection: (reason) => reportUncaught(reason, "unhandledRejection"),
29981
+ exitStatus: () => normalizeExitCode(processObject.exitCode),
29982
+ emitExit,
28922
29983
  writeStdin: (data) => {
28923
29984
  if (options.interactiveStdin) stdin.write(data);
28924
29985
  },
@@ -28980,7 +30041,7 @@ function createPathModule(cwd) {
28980
30041
  path.default = path;
28981
30042
  return path;
28982
30043
  }
28983
- function createFsModule(volume, cwd, stdinPath) {
30044
+ function createFsModule(volume, cwd, stdinPath, defer = queueMicrotask) {
28984
30045
  const fds = /* @__PURE__ */ new Map();
28985
30046
  let nextFd = 3;
28986
30047
  if (stdinPath) fds.set(0, { path: stdinPath, position: 0, flags: "r" });
@@ -29020,12 +30081,15 @@ function createFsModule(volume, cwd, stdinPath) {
29020
30081
  volume.rmdirSync(path);
29021
30082
  };
29022
30083
  const callback = (operation, cb) => {
29023
- queueMicrotask(() => {
30084
+ defer(() => {
30085
+ let value;
29024
30086
  try {
29025
- cb(null, operation());
30087
+ value = operation();
29026
30088
  } catch (error) {
29027
30089
  cb(error);
30090
+ return;
29028
30091
  }
30092
+ cb(null, value);
29029
30093
  });
29030
30094
  };
29031
30095
  const fs = {
@@ -29155,7 +30219,7 @@ function createFsModule(volume, cwd, stdinPath) {
29155
30219
  const slice = whole.subarray(start2, Math.max(start2, end));
29156
30220
  const stream = streamModule4.Readable.from([settings.encoding ? slice.toString(settings.encoding) : slice]);
29157
30221
  Object.assign(stream, { path: target, bytesRead: slice.length, close: () => stream.destroy() });
29158
- queueMicrotask(() => {
30222
+ defer(() => {
29159
30223
  stream.emit("open", 0);
29160
30224
  stream.emit("ready");
29161
30225
  });
@@ -29223,7 +30287,7 @@ function createFsModule(volume, cwd, stdinPath) {
29223
30287
  }
29224
30288
  fs.realpath.native = fs.realpath;
29225
30289
  fs.exists = (path, cb) => {
29226
- queueMicrotask(() => cb(fs.existsSync(path)));
30290
+ defer(() => cb(fs.existsSync(path)));
29227
30291
  };
29228
30292
  fs.promises = {
29229
30293
  ...Object.fromEntries(
@@ -29345,7 +30409,7 @@ var Console = class {
29345
30409
  timeLog = (_label = "default") => {
29346
30410
  };
29347
30411
  };
29348
- function createDnsModule() {
30412
+ function createDnsModule(defer = queueMicrotask) {
29349
30413
  const LOOPBACK = {
29350
30414
  localhost: { address: "127.0.0.1", family: 4 },
29351
30415
  "127.0.0.1": { address: "127.0.0.1", family: 4 },
@@ -29365,7 +30429,7 @@ function createDnsModule() {
29365
30429
  const lookup = (hostname, options, callback) => {
29366
30430
  const done = typeof options === "function" ? options : callback;
29367
30431
  const all = typeof options === "object" && options !== null && options.all;
29368
- queueMicrotask(() => {
30432
+ defer(() => {
29369
30433
  try {
29370
30434
  const entry = lookupSync(hostname);
29371
30435
  done?.(null, all ? [entry] : entry.address, entry.family);
@@ -29523,7 +30587,16 @@ var ERRNO_CONSTANTS = {
29523
30587
  EWOULDBLOCK: 11,
29524
30588
  EXDEV: 18
29525
30589
  };
29526
- function createTrackedTimers() {
30590
+ function createTrackedTimers(onError = (error) => {
30591
+ throw error;
30592
+ }) {
30593
+ const run2 = (fn, args) => {
30594
+ try {
30595
+ fn(...args);
30596
+ } catch (error) {
30597
+ onError(error);
30598
+ }
30599
+ };
29527
30600
  const live = /* @__PURE__ */ new Set();
29528
30601
  const unrefed = /* @__PURE__ */ new Set();
29529
30602
  const states = /* @__PURE__ */ new WeakMap();
@@ -29566,7 +30639,7 @@ function createTrackedTimers() {
29566
30639
  const native = setTimeout(
29567
30640
  (...inner) => {
29568
30641
  complete(handle);
29569
- fn(...inner);
30642
+ run2(fn, inner);
29570
30643
  },
29571
30644
  delay,
29572
30645
  ...args
@@ -29574,14 +30647,14 @@ function createTrackedTimers() {
29574
30647
  handle = track(native);
29575
30648
  return handle;
29576
30649
  };
29577
- const setIntervalTracked = (fn, delay, ...args) => track(setInterval(fn, delay, ...args));
30650
+ const setIntervalTracked = (fn, delay, ...args) => track(setInterval((...inner) => run2(fn, inner), delay, ...args));
29578
30651
  const hostSetImmediate = globalThis.setImmediate;
29579
30652
  const setImmediateTracked = (fn, ...args) => {
29580
30653
  if (!hostSetImmediate) return setTimeoutTracked(fn, 0, ...args);
29581
30654
  let handle;
29582
30655
  const native = hostSetImmediate((...inner) => {
29583
30656
  complete(handle);
29584
- fn(...inner);
30657
+ run2(fn, inner);
29585
30658
  }, ...args);
29586
30659
  handle = track(native);
29587
30660
  return handle;
@@ -29764,6 +30837,43 @@ function createNetModule() {
29764
30837
  isIP: (value) => isIPv4(value) ? 4 : isIPv6(value) ? 6 : 0
29765
30838
  });
29766
30839
  }
30840
+ function parseTestSettings(raw) {
30841
+ if (!raw) return {};
30842
+ try {
30843
+ const parsed = JSON.parse(raw);
30844
+ return parsed && typeof parsed === "object" ? parsed : {};
30845
+ } catch {
30846
+ return {};
30847
+ }
30848
+ }
30849
+ var EVENT_EMITTER_METHODS = [
30850
+ "on",
30851
+ "addListener",
30852
+ "once",
30853
+ "off",
30854
+ "removeListener",
30855
+ "removeAllListeners",
30856
+ "emit",
30857
+ "prependListener",
30858
+ "prependOnceListener",
30859
+ "listeners",
30860
+ "rawListeners",
30861
+ "listenerCount",
30862
+ "eventNames",
30863
+ "setMaxListeners",
30864
+ "getMaxListeners"
30865
+ ];
30866
+ function normalizeExitCode(value) {
30867
+ const code = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
30868
+ return typeof code === "number" && Number.isInteger(code) ? code & 255 : 0;
30869
+ }
30870
+ function formatUncaught(error) {
30871
+ const text2 = error instanceof Error ? error.stack ?? `${error.name}: ${error.message}` : `Uncaught ${typeof error === "string" ? JSON.stringify(error) : String(error)}`;
30872
+ return `${text2}
30873
+
30874
+ Node.js v22.12.0
30875
+ `;
30876
+ }
29767
30877
  var stubNames = ["cluster", "dgram", "diagnostics_channel", "domain", "http2", "inspector", "net", "tls", "v8", "vm", "worker_threads"];
29768
30878
  var builtinNames2 = [
29769
30879
  "assert",
@@ -29801,6 +30911,7 @@ var builtinNames2 = [
29801
30911
  "util",
29802
30912
  "util/types",
29803
30913
  "zlib",
30914
+ "test",
29804
30915
  ...stubNames
29805
30916
  ];
29806
30917
 
@@ -30622,16 +31733,20 @@ async function loadNodeRolldownMemfsBinding() {
30622
31733
  return null;
30623
31734
  }
30624
31735
  const fromBinding = createRequire(join3(packageDir, "package.json"));
30625
- let runtime;
31736
+ let runtimeModuleUrl2;
30626
31737
  let fsModuleUrl;
30627
- let createContext2;
30628
31738
  try {
30629
- runtime = fromBinding("@napi-rs/wasm-runtime");
31739
+ runtimeModuleUrl2 = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", ".");
30630
31740
  fsModuleUrl = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", "./fs");
30631
- ({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
30632
31741
  } catch {
30633
31742
  return null;
30634
31743
  }
31744
+ const runtime = await import(
31745
+ /* @vite-ignore */
31746
+ /* webpackIgnore: true */
31747
+ runtimeModuleUrl2
31748
+ );
31749
+ const createContext2 = runtime.createContext;
30635
31750
  const { memfs } = await import(
30636
31751
  /* @vite-ignore */
30637
31752
  /* webpackIgnore: true */
@@ -30663,7 +31778,7 @@ async function loadNodeRolldownMemfsBinding() {
30663
31778
  onCreateWorker() {
30664
31779
  const worker = new Worker2(workerPath, {
30665
31780
  env: process.env,
30666
- workerData: { runtimeAnchor, fsModuleUrl },
31781
+ workerData: { runtimeAnchor, runtimeModuleUrl: runtimeModuleUrl2, fsModuleUrl },
30667
31782
  /*
30668
31783
  * Deliberately no `execArgv`.
30669
31784
  *
@@ -31106,6 +32221,8 @@ var LocalRuntimePod = class _LocalRuntimePod {
31106
32221
  rolldownBinding;
31107
32222
  /** Backs outbound `http`/`https` client requests from inside the sandbox. */
31108
32223
  fetch;
32224
+ /** What guest programs may reach outside the container. */
32225
+ networkPolicy;
31109
32226
  constructor(options) {
31110
32227
  ensureProcessGlobal();
31111
32228
  this.sockets = new VirtualTcpNetwork((port) => this.router.activePortsIncludes(port));
@@ -31123,6 +32240,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
31123
32240
  this.router.onListen = (port) => notify(port, `http://localhost:${port}`);
31124
32241
  }
31125
32242
  if (options.fetch) this.fetch = options.fetch;
32243
+ if (options.network) this.networkPolicy = { ...options.network };
31126
32244
  this.packages = new CleanPackageInstaller(this.volume, {
31127
32245
  cwd: this.workdir,
31128
32246
  ...options.registry ? { registry: options.registry } : {},
@@ -31133,6 +32251,10 @@ var LocalRuntimePod = class _LocalRuntimePod {
31133
32251
  static async boot(options = {}) {
31134
32252
  return new _LocalRuntimePod(options);
31135
32253
  }
32254
+ /** Apply a container's outbound policy to programs started from now on. */
32255
+ setNetworkPolicy(policy) {
32256
+ this.networkPolicy = { ...policy };
32257
+ }
31136
32258
  async spawn(command, args = [], options = {}) {
31137
32259
  this.assertActive();
31138
32260
  if (command !== "node" && command !== "nodejs") throw commandError(command);
@@ -31145,7 +32267,6 @@ var LocalRuntimePod = class _LocalRuntimePod {
31145
32267
  await this.prepareRolldown(cwd, env2);
31146
32268
  if (proc.isKilled()) return 137;
31147
32269
  const untrack = trackProcess(proc);
31148
- let requestedExit = 0;
31149
32270
  let engine;
31150
32271
  const core = createCoreModules({
31151
32272
  volume: this.volume,
@@ -31159,11 +32280,15 @@ var LocalRuntimePod = class _LocalRuntimePod {
31159
32280
  * thrown from a later callback it would escape into whichever library
31160
32281
  * scheduled that callback and look like an unrelated crash. */
31161
32282
  onExit: (code) => {
31162
- requestedExit = code;
31163
32283
  proc.exitNow(code);
31164
32284
  if (engine?.isEvaluating) throw new ProcessExit(code);
31165
32285
  },
31166
- http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
32286
+ http: {
32287
+ router: this.router,
32288
+ owner,
32289
+ ...this.fetch ? { fetch: this.fetch } : {},
32290
+ ...this.networkPolicy ? { policy: this.networkPolicy } : {}
32291
+ },
31167
32292
  spawnChild: (config2) => this.processManager.spawn(config2),
31168
32293
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
31169
32294
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
@@ -31187,7 +32312,9 @@ var LocalRuntimePod = class _LocalRuntimePod {
31187
32312
  try {
31188
32313
  await Promise.race([engine.run(script), proc.waitForKill()]);
31189
32314
  await this.settle(owner, core.pendingHandles, core.readingStdin, () => core.pendingRequests() + hostWork.pending(), () => proc.isKilled());
31190
- return requestedExit;
32315
+ const status = core.exitStatus();
32316
+ core.emitExit(status);
32317
+ return status;
31191
32318
  } finally {
31192
32319
  hostWork.dispose();
31193
32320
  core.cancelTimers();
@@ -31690,6 +32817,9 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31690
32817
  case "http-response":
31691
32818
  this.settleProxied(Number(message.id), message.response);
31692
32819
  return;
32820
+ case "loopback-request":
32821
+ void this.answerLoopback(worker, message);
32822
+ return;
31693
32823
  case "ws-data":
31694
32824
  this.upgraded.get(Number(message.id))?.write(new Uint8Array(message.data));
31695
32825
  return;
@@ -31725,7 +32855,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31725
32855
  aliases: this.aliases,
31726
32856
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
31727
32857
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
31728
- ...options.tty ? { tty: true } : {}
32858
+ ...options.tty ? { tty: true } : {},
32859
+ ...this.networkPolicy ? { network: this.networkPolicy } : {}
31729
32860
  });
31730
32861
  process2.begin();
31731
32862
  return process2;
@@ -31861,6 +32992,38 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
31861
32992
  init: { method: request.method, path: request.url, headers: request.headers }
31862
32993
  });
31863
32994
  }
32995
+ /**
32996
+ * Answer a program that is calling a server in this container.
32997
+ *
32998
+ * A guest's router holds only the servers on its own thread, so a request to
32999
+ * a port some other process opened comes here, where every server in the
33000
+ * container is registered. `null` means nothing listens, which the guest
33001
+ * reports as a refused connection.
33002
+ */
33003
+ async answerLoopback(worker, message) {
33004
+ const port = Number(message.port);
33005
+ let response = null;
33006
+ if (this.router.activePortsIncludes(port)) {
33007
+ try {
33008
+ const result = await this.router.request(port, message.request);
33009
+ const body = result.body;
33010
+ response = {
33011
+ statusCode: result.statusCode,
33012
+ statusMessage: result.statusMessage,
33013
+ headers: result.headers,
33014
+ body: typeof body === "string" ? new TextEncoder().encode(body) : body instanceof ArrayBuffer ? new Uint8Array(body) : body ?? new Uint8Array()
33015
+ };
33016
+ } catch (error) {
33017
+ response = {
33018
+ statusCode: 502,
33019
+ statusMessage: "Bad Gateway",
33020
+ headers: {},
33021
+ body: new TextEncoder().encode(error instanceof Error ? error.message : String(error))
33022
+ };
33023
+ }
33024
+ }
33025
+ worker.postMessage({ type: "loopback-response", id: message.id, response });
33026
+ }
31864
33027
  settleProxied(id, response) {
31865
33028
  const resolve3 = this.waiting.get(id);
31866
33029
  if (!resolve3) return;
@@ -31951,13 +33114,19 @@ var Container = class _Container {
31951
33114
  // ── boot ──────────────────────────────────────────────────────────────────
31952
33115
  static async create(opts = {}) {
31953
33116
  if (opts.python) configurePython(opts.python);
33117
+ const network = {
33118
+ allowOutbound: opts.network?.allowOutbound ?? false,
33119
+ allowedHosts: opts.network?.allowedHosts ?? null
33120
+ };
31954
33121
  const podOptions = {
31955
33122
  workdir: opts.cwd ?? "/",
31956
33123
  env: opts.env ?? {},
33124
+ network,
31957
33125
  ...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
31958
33126
  };
31959
33127
  const workerOptions = { ...podOptions, ...opts.workerUrl ? { workerUrl: opts.workerUrl } : {} };
31960
33128
  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);
33129
+ if (opts.pod) opts.pod.setNetworkPolicy?.(network);
31961
33130
  const kernel = new Kernel({
31962
33131
  pod,
31963
33132
  hostname: opts.hostname ?? "sandbox",
@@ -32052,7 +33221,7 @@ var Container = class _Container {
32052
33221
  async exec(command, opts = {}) {
32053
33222
  this.assertActive();
32054
33223
  const started = Date.now();
32055
- const { io, stdout, stderr, combined } = this.makeStdio(opts);
33224
+ const { io, stdout, stderr, combined, detach } = this.makeStdio(opts);
32056
33225
  const cred = opts.user ? this.kernel.users.credFor(opts.user) : this.defaults.cred;
32057
33226
  const env2 = { ...this.defaults.env, ...opts.env };
32058
33227
  const cwd = opts.cwd ? resolve(this.defaults.cwd, opts.cwd) : this.defaults.cwd;
@@ -32081,6 +33250,7 @@ var Container = class _Container {
32081
33250
  ]);
32082
33251
  } finally {
32083
33252
  if (timer) clearTimeout(timer);
33253
+ detach();
32084
33254
  proc.exit(0);
32085
33255
  this.kernel.procs.remove(proc.pid);
32086
33256
  }
@@ -32097,7 +33267,7 @@ var Container = class _Container {
32097
33267
  async run(argv, opts = {}) {
32098
33268
  this.assertActive();
32099
33269
  const started = Date.now();
32100
- const { io, stdout, stderr, combined } = this.makeStdio(opts);
33270
+ const { io, stdout, stderr, combined, detach } = this.makeStdio(opts);
32101
33271
  const cred = opts.user ? this.kernel.users.credFor(opts.user) : this.defaults.cred;
32102
33272
  const cwd = opts.cwd ? resolve(this.defaults.cwd, opts.cwd) : this.defaults.cwd;
32103
33273
  const result = await this.kernel.run(argv, {
@@ -32109,6 +33279,7 @@ var Container = class _Container {
32109
33279
  stderr: io.stderr,
32110
33280
  ...opts.timeoutMs ?? this.defaults.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs ?? this.defaults.timeoutMs } : {}
32111
33281
  });
33282
+ detach();
32112
33283
  return {
32113
33284
  exitCode: result.exitCode,
32114
33285
  stdout: stdout.text(),
@@ -32199,6 +33370,14 @@ var Container = class _Container {
32199
33370
  if (!this.defaultSession) this.defaultSession = this.session();
32200
33371
  return this.defaultSession;
32201
33372
  }
33373
+ /**
33374
+ * The streams one call collects into.
33375
+ *
33376
+ * They are detachable because a background job started by the command keeps
33377
+ * writing after the call has returned. Nothing reads the result any more by
33378
+ * then, so without `detach` a chatty server would grow the buffer for as long
33379
+ * as it ran; afterwards its output still reaches the container-wide taps.
33380
+ */
32202
33381
  makeStdio(opts) {
32203
33382
  const combined = [];
32204
33383
  const decoder9 = new TextDecoder();
@@ -32214,14 +33393,25 @@ var Container = class _Container {
32214
33393
  opts.onStderr?.(text2);
32215
33394
  this.hooks.onStderr?.(text2);
32216
33395
  });
33396
+ const outGate = new DetachableOutput(stdout, (text2) => this.hooks.onStdout?.(text2));
33397
+ const errGate = new DetachableOutput(stderr, (text2) => this.hooks.onStderr?.(text2));
32217
33398
  if (opts.tty) {
32218
- stdout.isTTY = true;
32219
- stderr.isTTY = true;
32220
- stdout.columns = opts.columns ?? 80;
32221
- stdout.rows = opts.rows ?? 24;
33399
+ outGate.isTTY = true;
33400
+ errGate.isTTY = true;
33401
+ outGate.columns = opts.columns ?? 80;
33402
+ outGate.rows = opts.rows ?? 24;
32222
33403
  }
32223
33404
  const stdin = opts.stdin === void 0 ? new NullInput() : Pipe.from(opts.stdin);
32224
- return { io: { stdin, stdout, stderr }, stdout, stderr, combined };
33405
+ return {
33406
+ io: { stdin, stdout: outGate, stderr: errGate },
33407
+ stdout,
33408
+ stderr,
33409
+ combined,
33410
+ detach: () => {
33411
+ outGate.detach();
33412
+ errGate.detach();
33413
+ }
33414
+ };
32225
33415
  }
32226
33416
  // ── networking ────────────────────────────────────────────────────────────
32227
33417
  /** Send an HTTP request to a server running inside the container. */