sandboxedjs 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -757,10 +757,10 @@ function globToRegexSource(pattern, opts = {}) {
757
757
  continue;
758
758
  }
759
759
  if (c === "[") {
760
- const compiled2 = compileBracket(pattern, i);
761
- if (compiled2) {
762
- out += guardDot() + compiled2.source;
763
- i = compiled2.next;
760
+ const compiled3 = compileBracket(pattern, i);
761
+ if (compiled3) {
762
+ out += guardDot() + compiled3.source;
763
+ i = compiled3.next;
764
764
  atSegmentStart = false;
765
765
  continue;
766
766
  }
@@ -6716,8 +6716,8 @@ var init_ffmpeg_core = __esm({
6716
6716
  };
6717
6717
  function handleMessage(data) {
6718
6718
  if (typeof data == "string") {
6719
- var encoder6 = new TextEncoder();
6720
- data = encoder6.encode(data);
6719
+ var encoder7 = new TextEncoder();
6720
+ data = encoder7.encode(data);
6721
6721
  } else {
6722
6722
  assert(data.byteLength !== void 0);
6723
6723
  if (data.byteLength == 0) {
@@ -10443,12 +10443,40 @@ function strerrorOf(e) {
10443
10443
  var EXIT_NOT_EXECUTABLE = 126;
10444
10444
  var EXIT_NOT_FOUND = 127;
10445
10445
 
10446
+ // src/kernel/binfmt.ts
10447
+ var MAGIC_BYTES = 256;
10448
+ var ExecFormatRegistry = class {
10449
+ formats = [];
10450
+ register(format) {
10451
+ this.formats.push(format);
10452
+ this.formats.sort((a, b) => (a.priority ?? 50) - (b.priority ?? 50));
10453
+ }
10454
+ registerAll(formats) {
10455
+ for (const f of formats) this.register(f);
10456
+ }
10457
+ /** The handler claiming `head`, or null when the bytes match nothing. */
10458
+ match(head2, path) {
10459
+ for (const format of this.formats) {
10460
+ try {
10461
+ if (format.matches(head2, path)) return format;
10462
+ } catch {
10463
+ }
10464
+ }
10465
+ return null;
10466
+ }
10467
+ list() {
10468
+ return this.formats.slice();
10469
+ }
10470
+ };
10471
+
10446
10472
  // src/kernel/kernel.ts
10447
10473
  var BUILTIN_INTERPRETER = "/proc/self/exe";
10448
10474
  var Kernel = class {
10449
10475
  vfs;
10450
10476
  procs = new ProcessTable();
10451
10477
  commands = new CommandRegistry();
10478
+ /** Handlers for executable files that are neither built-ins nor scripts. */
10479
+ formats = new ExecFormatRegistry();
10452
10480
  users;
10453
10481
  pod;
10454
10482
  bootTime;
@@ -10578,14 +10606,15 @@ var Kernel = class {
10578
10606
  const command = this.commands.get(builtinName);
10579
10607
  if (command) return { kind: "builtin", path: candidate, command };
10580
10608
  }
10581
- let head2 = "";
10609
+ let bytes = new Uint8Array(0);
10582
10610
  try {
10583
10611
  if (this.vfs.permitted(st, R_OK, cred)) {
10584
- head2 = new TextDecoder().decode(this.vfs.readFile(real, cred).subarray(0, 256));
10612
+ bytes = this.vfs.readFile(real, cred).subarray(0, MAGIC_BYTES);
10585
10613
  }
10586
10614
  } catch {
10587
10615
  }
10588
- if (head2.startsWith("#!")) {
10616
+ if (bytes[0] === 35 && bytes[1] === 33) {
10617
+ const head2 = new TextDecoder().decode(bytes);
10589
10618
  const lineEnd = head2.indexOf("\n");
10590
10619
  const line = (lineEnd >= 0 ? head2.slice(2, lineEnd) : head2.slice(2)).trim();
10591
10620
  const parts = line.split(/\s+/).filter(Boolean);
@@ -10595,6 +10624,8 @@ var Kernel = class {
10595
10624
  }
10596
10625
  if (parts.length) return { kind: "script", path: real, interpreter: parts };
10597
10626
  }
10627
+ const format = this.formats.match(bytes, real);
10628
+ if (format) return { kind: "binary", path: real, format, head: bytes };
10598
10629
  const ext = extname(real);
10599
10630
  if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return { kind: "script", path: real, interpreter: ["/usr/bin/node"] };
10600
10631
  if (ext === ".py") return { kind: "script", path: real, interpreter: ["/usr/bin/python3"] };
@@ -10747,6 +10778,24 @@ var Kernel = class {
10747
10778
  proc.argv = [interp, ...interpArgs, resolved.path, ...proc.argv.slice(1)];
10748
10779
  return await this.dispatch(proc, depth + 1);
10749
10780
  }
10781
+ if (resolved.kind === "binary" && resolved.format) {
10782
+ const { format } = resolved;
10783
+ if (format.run) {
10784
+ const ctx = createContext({ argv: proc.argv, proc, kernel: this });
10785
+ const previous = this._current;
10786
+ this._current = proc;
10787
+ try {
10788
+ return await format.run(ctx, resolved.path);
10789
+ } finally {
10790
+ this._current = previous;
10791
+ }
10792
+ }
10793
+ const lines = format.explain?.(resolved.head ?? new Uint8Array(0), resolved.path) ?? [
10794
+ `${name}: cannot execute ${format.describe?.(resolved.head ?? new Uint8Array(0), resolved.path) ?? format.name} file`
10795
+ ];
10796
+ for (const line of lines) proc.stderr.write(line + "\n");
10797
+ return EXIT_NOT_EXECUTABLE;
10798
+ }
10750
10799
  proc.stderr.write(`${name}: cannot execute binary file: Exec format error
10751
10800
  `);
10752
10801
  return EXIT_NOT_EXECUTABLE;
@@ -13523,16 +13572,16 @@ sys ${fmt2(Math.floor(ms * 0.2))}
13523
13572
  function splitAliasWords(text) {
13524
13573
  const out = [];
13525
13574
  let current = "";
13526
- let quote2 = null;
13575
+ let quote3 = null;
13527
13576
  for (let i = 0; i < text.length; i++) {
13528
13577
  const c = text[i];
13529
- if (quote2) {
13530
- if (c === quote2) quote2 = null;
13578
+ if (quote3) {
13579
+ if (c === quote3) quote3 = null;
13531
13580
  else current += c;
13532
13581
  continue;
13533
13582
  }
13534
13583
  if (c === "'" || c === '"') {
13535
- quote2 = c;
13584
+ quote3 = c;
13536
13585
  continue;
13537
13586
  }
13538
13587
  if (c === " " || c === " ") {
@@ -14906,8 +14955,10 @@ function describeFile(ctx, abs, st, mime) {
14906
14955
  if (st.size === 0) return mime ? "inode/x-empty" : "empty";
14907
14956
  const bytes = ctx.vfs.readFile(abs, ctx.cred).subarray(0, 512);
14908
14957
  const magic = [...bytes.subarray(0, 4)];
14909
- if (magic[0] === 127 && magic[1] === 69 && magic[2] === 76 && magic[3] === 70) {
14910
- return mime ? "application/x-executable" : "ELF 64-bit LSB executable, x86-64";
14958
+ const format = ctx.kernel.formats.match(bytes, abs);
14959
+ if (format) {
14960
+ if (mime) return format.name === "wasm" ? "application/wasm" : "application/x-executable";
14961
+ return format.describe?.(bytes, abs) ?? `${format.name} executable`;
14911
14962
  }
14912
14963
  if (magic[0] === 31 && magic[1] === 139) return mime ? "application/gzip" : "gzip compressed data";
14913
14964
  if (magic[0] === 80 && magic[1] === 75) return mime ? "application/zip" : "Zip archive data";
@@ -22544,16 +22595,16 @@ async function loadFactory() {
22544
22595
  const path = await nodeBuiltin("path");
22545
22596
  const require2 = createRequire(path.join(process.cwd(), "index.js"));
22546
22597
  const wasmPath = require2.resolve("@ffmpeg/core/wasm");
22547
- const wasm = await fs.readFile(wasmPath);
22598
+ const wasm2 = await fs.readFile(wasmPath);
22548
22599
  const module = await Promise.resolve().then(() => (init_ffmpeg_core(), ffmpeg_core_exports));
22549
- return { factory: module.default, wasm: new Uint8Array(wasm) };
22600
+ return { factory: module.default, wasm: new Uint8Array(wasm2) };
22550
22601
  }
22551
22602
  function getCompiled() {
22552
22603
  compiled ??= (async () => {
22553
- const { factory, wasm } = await loadFactory();
22554
- const bytes = wasm.buffer.slice(
22555
- wasm.byteOffset,
22556
- wasm.byteOffset + wasm.byteLength
22604
+ const { factory, wasm: wasm2 } = await loadFactory();
22605
+ const bytes = wasm2.buffer.slice(
22606
+ wasm2.byteOffset,
22607
+ wasm2.byteOffset + wasm2.byteLength
22557
22608
  );
22558
22609
  return { factory, module: await WebAssembly.compile(bytes) };
22559
22610
  })();
@@ -22630,6 +22681,1019 @@ var ffprobe = defineCommand({
22630
22681
  function ffmpegCommands() {
22631
22682
  return [ffmpeg, ffprobe];
22632
22683
  }
22684
+
22685
+ // src/runtime/wasi-preview1.ts
22686
+ init_errno();
22687
+ init_mode();
22688
+ init_path();
22689
+ var E = {
22690
+ SUCCESS: 0,
22691
+ ACCES: 2,
22692
+ AGAIN: 6,
22693
+ BADF: 8,
22694
+ BUSY: 10,
22695
+ EXIST: 20,
22696
+ FAULT: 21,
22697
+ INVAL: 28,
22698
+ IO: 29,
22699
+ ISDIR: 31,
22700
+ LOOP: 32,
22701
+ MFILE: 33,
22702
+ NAMETOOLONG: 37,
22703
+ NOENT: 44,
22704
+ NOEXEC: 45,
22705
+ NOMEM: 48,
22706
+ NOSPC: 51,
22707
+ NOSYS: 52,
22708
+ NOTDIR: 54,
22709
+ NOTEMPTY: 55,
22710
+ NOTSUP: 58,
22711
+ NOTTY: 59,
22712
+ PERM: 63,
22713
+ PIPE: 64,
22714
+ ROFS: 69,
22715
+ SPIPE: 70,
22716
+ XDEV: 75,
22717
+ NOTCAPABLE: 76
22718
+ };
22719
+ var ERRNO_MAP = {
22720
+ EPERM: E.PERM,
22721
+ ENOENT: E.NOENT,
22722
+ EIO: E.IO,
22723
+ EBADF: E.BADF,
22724
+ EACCES: E.ACCES,
22725
+ EBUSY: E.BUSY,
22726
+ EEXIST: E.EXIST,
22727
+ EXDEV: E.XDEV,
22728
+ ENODEV: E.NOSYS,
22729
+ ENOTDIR: E.NOTDIR,
22730
+ EISDIR: E.ISDIR,
22731
+ EINVAL: E.INVAL,
22732
+ ENFILE: E.MFILE,
22733
+ EMFILE: E.MFILE,
22734
+ ENOSPC: E.NOSPC,
22735
+ EROFS: E.ROFS,
22736
+ ENOTEMPTY: E.NOTEMPTY,
22737
+ ELOOP: E.LOOP,
22738
+ ENOSYS: E.NOSYS,
22739
+ ENAMETOOLONG: E.NAMETOOLONG,
22740
+ EPIPE: E.PIPE,
22741
+ ENOTSUP: E.NOTSUP,
22742
+ ESPIPE: E.SPIPE
22743
+ };
22744
+ var FT = {
22745
+ UNKNOWN: 0,
22746
+ BLOCK_DEVICE: 1,
22747
+ CHARACTER_DEVICE: 2,
22748
+ DIRECTORY: 3,
22749
+ REGULAR_FILE: 4,
22750
+ SOCKET_STREAM: 6,
22751
+ SYMBOLIC_LINK: 7
22752
+ };
22753
+ var O_CREAT = 1 << 0;
22754
+ var O_DIRECTORY = 1 << 1;
22755
+ var O_EXCL = 1 << 2;
22756
+ var O_TRUNC = 1 << 3;
22757
+ var FD_APPEND = 1 << 0;
22758
+ var LOOKUP_SYMLINK_FOLLOW = 1 << 0;
22759
+ var RIGHT_FD_READ = 1n << 1n;
22760
+ var RIGHT_FD_WRITE = 1n << 6n;
22761
+ var RIGHTS_ALL = (1n << 63n) - 1n;
22762
+ var WHENCE_SET = 0;
22763
+ var WHENCE_CUR = 1;
22764
+ var WHENCE_END = 2;
22765
+ var SIZEOF_DIRENT = 24;
22766
+ var SIZEOF_FILESTAT = 64;
22767
+ var SIZEOF_IOVEC = 8;
22768
+ var WasiExit = class extends Error {
22769
+ constructor(code) {
22770
+ super(`wasi exit ${code}`);
22771
+ this.code = code;
22772
+ this.name = "WasiExit";
22773
+ }
22774
+ code;
22775
+ };
22776
+ var encoder6 = new TextEncoder();
22777
+ var decoder7 = new TextDecoder();
22778
+ var WasiPreview1 = class {
22779
+ memory = null;
22780
+ fds = /* @__PURE__ */ new Map();
22781
+ nextFd = 3;
22782
+ opts;
22783
+ now;
22784
+ randomFill;
22785
+ /** Set once `proc_exit` has been seen, so the caller can tell exit 0 from a
22786
+ * module that simply returned. */
22787
+ exited = null;
22788
+ constructor(opts) {
22789
+ this.opts = opts;
22790
+ this.now = opts.now ?? Date.now;
22791
+ this.randomFill = opts.randomFill ?? ((buffer) => {
22792
+ for (let i = 0; i < buffer.length; i++) buffer[i] = Math.floor(Math.random() * 256);
22793
+ });
22794
+ const blank = { pos: 0, data: new Uint8Array(0), dirty: false, fdflags: 0, rights: RIGHTS_ALL };
22795
+ this.fds.set(0, { kind: "stdin", path: "/dev/stdin", ...blank, data: opts.stdin ?? new Uint8Array(0) });
22796
+ this.fds.set(1, { kind: "stdout", path: "/dev/stdout", ...blank });
22797
+ this.fds.set(2, { kind: "stderr", path: "/dev/stderr", ...blank });
22798
+ const preopens = opts.preopens ?? { "/": "/", ".": opts.cwd };
22799
+ for (const [name, path] of Object.entries(preopens)) {
22800
+ const fd = this.nextFd++;
22801
+ this.fds.set(fd, { kind: "dir", path, ...blank, preopen: name });
22802
+ }
22803
+ }
22804
+ /** Give the host access to the instantiated module's memory. */
22805
+ bind(instance) {
22806
+ const memory = instance.exports.memory;
22807
+ if (!(memory instanceof WebAssembly.Memory)) {
22808
+ throw new Error("wasm module does not export its memory; not a WASI command module");
22809
+ }
22810
+ this.memory = memory;
22811
+ }
22812
+ /* ── memory access ──────────────────────────────────────────────────────
22813
+ * Re-derived on every use: a module that grows its memory detaches the old
22814
+ * ArrayBuffer, and a cached view would silently start reading zeroes. */
22815
+ get view() {
22816
+ if (!this.memory) throw new Error("wasi host used before bind()");
22817
+ return new DataView(this.memory.buffer);
22818
+ }
22819
+ get bytes() {
22820
+ if (!this.memory) throw new Error("wasi host used before bind()");
22821
+ return new Uint8Array(this.memory.buffer);
22822
+ }
22823
+ readString(ptr, len) {
22824
+ return decoder7.decode(this.bytes.subarray(ptr, ptr + len));
22825
+ }
22826
+ /** Scatter/gather list, as `fd_read` and `fd_write` take. */
22827
+ iovecs(ptr, count) {
22828
+ const view = this.view;
22829
+ const out = [];
22830
+ for (let i = 0; i < count; i++) {
22831
+ const at = ptr + i * SIZEOF_IOVEC;
22832
+ out.push({ base: view.getUint32(at, true), len: view.getUint32(at + 4, true) });
22833
+ }
22834
+ return out;
22835
+ }
22836
+ /* ── path handling ──────────────────────────────────────────────────────── */
22837
+ /**
22838
+ * Resolve a path supplied by the module against a directory descriptor.
22839
+ *
22840
+ * The result is confined to the descriptor's subtree: `..` that would climb
22841
+ * out of a preopen is rejected with ENOTCAPABLE, which is WASI's own answer
22842
+ * and stops a module reaching a directory it was never granted.
22843
+ */
22844
+ resolveAt(dirfd, path) {
22845
+ const dir3 = this.fds.get(dirfd);
22846
+ if (!dir3) return E.BADF;
22847
+ if (dir3.kind !== "dir") return E.NOTDIR;
22848
+ if (path === "") return E.NOENT;
22849
+ const abs = isAbsolute(path) ? normalize(path) : resolve(dir3.path, path);
22850
+ if (dir3.path !== "/" && !contains(dir3.path, abs) && abs !== dir3.path) return E.NOTCAPABLE;
22851
+ return { path: abs };
22852
+ }
22853
+ errnoOf(e) {
22854
+ if (isSysError(e)) return ERRNO_MAP[e.code] ?? E.IO;
22855
+ return E.IO;
22856
+ }
22857
+ filetypeOf(mode) {
22858
+ switch (mode & 61440) {
22859
+ case S_IFREG:
22860
+ return FT.REGULAR_FILE;
22861
+ case S_IFDIR:
22862
+ return FT.DIRECTORY;
22863
+ case S_IFLNK:
22864
+ return FT.SYMBOLIC_LINK;
22865
+ case S_IFCHR:
22866
+ return FT.CHARACTER_DEVICE;
22867
+ case S_IFBLK:
22868
+ return FT.BLOCK_DEVICE;
22869
+ case S_IFSOCK:
22870
+ return FT.SOCKET_STREAM;
22871
+ default:
22872
+ return FT.UNKNOWN;
22873
+ }
22874
+ }
22875
+ /** Write a 64-byte `filestat` for `path`. */
22876
+ writeFilestat(ptr, path, follow) {
22877
+ try {
22878
+ const st = follow ? this.opts.vfs.stat(path, { cred: this.opts.cred }) : this.opts.vfs.lstat(path);
22879
+ const view = this.view;
22880
+ view.setBigUint64(ptr, BigInt(st.dev), true);
22881
+ view.setBigUint64(ptr + 8, BigInt(st.ino), true);
22882
+ view.setUint8(ptr + 16, this.filetypeOf(st.mode));
22883
+ view.setBigUint64(ptr + 24, BigInt(st.nlink), true);
22884
+ view.setBigUint64(ptr + 32, BigInt(st.size), true);
22885
+ view.setBigUint64(ptr + 40, BigInt(Math.round(st.atimeMs * 1e6)), true);
22886
+ view.setBigUint64(ptr + 48, BigInt(Math.round(st.mtimeMs * 1e6)), true);
22887
+ view.setBigUint64(ptr + 56, BigInt(Math.round(st.ctimeMs * 1e6)), true);
22888
+ return E.SUCCESS;
22889
+ } catch (e) {
22890
+ return this.errnoOf(e);
22891
+ }
22892
+ }
22893
+ /** Persist an open file's buffer back to the filesystem. */
22894
+ flush(fd) {
22895
+ if (!fd.dirty) return E.SUCCESS;
22896
+ try {
22897
+ this.opts.vfs.writeFile(fd.path, fd.data, { cred: this.opts.cred });
22898
+ fd.dirty = false;
22899
+ return E.SUCCESS;
22900
+ } catch (e) {
22901
+ return this.errnoOf(e);
22902
+ }
22903
+ }
22904
+ /* ── the import object ──────────────────────────────────────────────────── */
22905
+ /**
22906
+ * The `wasi_snapshot_preview1` namespace, plus `wasi_unstable` for older
22907
+ * toolchains — the two differ only in `fd_seek`'s argument order, which is
22908
+ * handled by the alias below.
22909
+ */
22910
+ get imports() {
22911
+ const preview1 = this.syscalls();
22912
+ return {
22913
+ wasi_snapshot_preview1: preview1,
22914
+ wasi_unstable: {
22915
+ ...preview1,
22916
+ // preview0's fd_seek took (fd, offset, whence) with whence as the last
22917
+ // u32 but numbered CUR=0/END=1/SET=2 — the reverse of preview1.
22918
+ fd_seek: (fd, offset, whence, out) => preview1.fd_seek(fd, offset, whence === 0 ? WHENCE_CUR : whence === 1 ? WHENCE_END : WHENCE_SET, out)
22919
+ }
22920
+ };
22921
+ }
22922
+ /* eslint-disable @typescript-eslint/no-unused-vars */
22923
+ syscalls() {
22924
+ const vfs = this.opts.vfs;
22925
+ const cred = this.opts.cred;
22926
+ return {
22927
+ /* ── process ──────────────────────────────────────────────────────── */
22928
+ args_sizes_get: (countPtr, bufSizePtr) => {
22929
+ const view = this.view;
22930
+ view.setUint32(countPtr, this.opts.args.length, true);
22931
+ view.setUint32(
22932
+ bufSizePtr,
22933
+ this.opts.args.reduce((sum, a) => sum + encoder6.encode(a).length + 1, 0),
22934
+ true
22935
+ );
22936
+ return E.SUCCESS;
22937
+ },
22938
+ args_get: (argvPtr, bufPtr) => {
22939
+ const view = this.view;
22940
+ const bytes = this.bytes;
22941
+ let cursor = bufPtr;
22942
+ this.opts.args.forEach((arg, i) => {
22943
+ view.setUint32(argvPtr + i * 4, cursor, true);
22944
+ const encoded = encoder6.encode(arg);
22945
+ bytes.set(encoded, cursor);
22946
+ bytes[cursor + encoded.length] = 0;
22947
+ cursor += encoded.length + 1;
22948
+ });
22949
+ return E.SUCCESS;
22950
+ },
22951
+ environ_sizes_get: (countPtr, bufSizePtr) => {
22952
+ const pairs = this.envPairs();
22953
+ const view = this.view;
22954
+ view.setUint32(countPtr, pairs.length, true);
22955
+ view.setUint32(
22956
+ bufSizePtr,
22957
+ pairs.reduce((sum, p) => sum + encoder6.encode(p).length + 1, 0),
22958
+ true
22959
+ );
22960
+ return E.SUCCESS;
22961
+ },
22962
+ environ_get: (environPtr, bufPtr) => {
22963
+ const pairs = this.envPairs();
22964
+ const view = this.view;
22965
+ const bytes = this.bytes;
22966
+ let cursor = bufPtr;
22967
+ pairs.forEach((pair, i) => {
22968
+ view.setUint32(environPtr + i * 4, cursor, true);
22969
+ const encoded = encoder6.encode(pair);
22970
+ bytes.set(encoded, cursor);
22971
+ bytes[cursor + encoded.length] = 0;
22972
+ cursor += encoded.length + 1;
22973
+ });
22974
+ return E.SUCCESS;
22975
+ },
22976
+ proc_exit: (code) => {
22977
+ this.exited = code;
22978
+ throw new WasiExit(code);
22979
+ },
22980
+ proc_raise: (_signal) => E.NOTSUP,
22981
+ sched_yield: () => E.SUCCESS,
22982
+ random_get: (ptr, len) => {
22983
+ const target = new Uint8Array(len);
22984
+ this.randomFill(target);
22985
+ this.bytes.set(target, ptr);
22986
+ return E.SUCCESS;
22987
+ },
22988
+ /* ── clocks ───────────────────────────────────────────────────────── */
22989
+ clock_res_get: (_id, ptr) => {
22990
+ this.view.setBigUint64(ptr, 1000000n, true);
22991
+ return E.SUCCESS;
22992
+ },
22993
+ clock_time_get: (_id, _precision, ptr) => {
22994
+ this.view.setBigUint64(ptr, BigInt(Math.round(this.now() * 1e6)), true);
22995
+ return E.SUCCESS;
22996
+ },
22997
+ /* ── descriptors ──────────────────────────────────────────────────── */
22998
+ fd_close: (fd) => {
22999
+ const entry = this.fds.get(fd);
23000
+ if (!entry) return E.BADF;
23001
+ const status = this.flush(entry);
23002
+ this.fds.delete(fd);
23003
+ return status;
23004
+ },
23005
+ fd_datasync: (fd) => {
23006
+ const entry = this.fds.get(fd);
23007
+ return entry ? this.flush(entry) : E.BADF;
23008
+ },
23009
+ fd_sync: (fd) => {
23010
+ const entry = this.fds.get(fd);
23011
+ return entry ? this.flush(entry) : E.BADF;
23012
+ },
23013
+ fd_fdstat_get: (fd, ptr) => {
23014
+ const entry = this.fds.get(fd);
23015
+ if (!entry) return E.BADF;
23016
+ let filetype = FT.CHARACTER_DEVICE;
23017
+ if (entry.kind === "dir") filetype = FT.DIRECTORY;
23018
+ else if (entry.kind === "file") filetype = FT.REGULAR_FILE;
23019
+ const view = this.view;
23020
+ view.setUint8(ptr, filetype);
23021
+ view.setUint16(ptr + 2, entry.fdflags, true);
23022
+ view.setBigUint64(ptr + 8, entry.rights, true);
23023
+ view.setBigUint64(ptr + 16, entry.rights, true);
23024
+ return E.SUCCESS;
23025
+ },
23026
+ fd_fdstat_set_flags: (fd, flags) => {
23027
+ const entry = this.fds.get(fd);
23028
+ if (!entry) return E.BADF;
23029
+ entry.fdflags = flags;
23030
+ return E.SUCCESS;
23031
+ },
23032
+ fd_fdstat_set_rights: (fd, base, _inheriting) => {
23033
+ const entry = this.fds.get(fd);
23034
+ if (!entry) return E.BADF;
23035
+ entry.rights = base;
23036
+ return E.SUCCESS;
23037
+ },
23038
+ fd_filestat_get: (fd, ptr) => {
23039
+ const entry = this.fds.get(fd);
23040
+ if (!entry) return E.BADF;
23041
+ if (entry.kind === "stdin" || entry.kind === "stdout" || entry.kind === "stderr") {
23042
+ const view = this.view;
23043
+ new Uint8Array(this.memory.buffer, ptr, SIZEOF_FILESTAT).fill(0);
23044
+ view.setUint8(ptr + 16, FT.CHARACTER_DEVICE);
23045
+ view.setBigUint64(ptr + 32, BigInt(entry.data.length), true);
23046
+ return E.SUCCESS;
23047
+ }
23048
+ return this.writeFilestat(ptr, entry.path, true);
23049
+ },
23050
+ fd_filestat_set_size: (fd, size) => {
23051
+ const entry = this.fds.get(fd);
23052
+ if (!entry || entry.kind !== "file") return E.BADF;
23053
+ const length = Number(size);
23054
+ const next = new Uint8Array(length);
23055
+ next.set(entry.data.subarray(0, Math.min(length, entry.data.length)));
23056
+ entry.data = next;
23057
+ entry.dirty = true;
23058
+ return this.flush(entry);
23059
+ },
23060
+ fd_filestat_set_times: (fd, atim, mtim, _flags) => {
23061
+ const entry = this.fds.get(fd);
23062
+ if (!entry) return E.BADF;
23063
+ try {
23064
+ vfs.utimes(entry.path, Number(atim) / 1e6, Number(mtim) / 1e6, cred);
23065
+ return E.SUCCESS;
23066
+ } catch (e) {
23067
+ return this.errnoOf(e);
23068
+ }
23069
+ },
23070
+ fd_prestat_get: (fd, ptr) => {
23071
+ const entry = this.fds.get(fd);
23072
+ if (!entry || entry.preopen === void 0) return E.BADF;
23073
+ const view = this.view;
23074
+ view.setUint8(
23075
+ ptr,
23076
+ 0
23077
+ /* dir */
23078
+ );
23079
+ view.setUint32(ptr + 4, encoder6.encode(entry.preopen).length, true);
23080
+ return E.SUCCESS;
23081
+ },
23082
+ fd_prestat_dir_name: (fd, ptr, len) => {
23083
+ const entry = this.fds.get(fd);
23084
+ if (!entry || entry.preopen === void 0) return E.BADF;
23085
+ const name = encoder6.encode(entry.preopen);
23086
+ if (name.length > len) return E.NAMETOOLONG;
23087
+ this.bytes.set(name, ptr);
23088
+ return E.SUCCESS;
23089
+ },
23090
+ fd_renumber: (from, to) => {
23091
+ const entry = this.fds.get(from);
23092
+ if (!entry) return E.BADF;
23093
+ const existing = this.fds.get(to);
23094
+ if (existing) this.flush(existing);
23095
+ this.fds.set(to, entry);
23096
+ this.fds.delete(from);
23097
+ return E.SUCCESS;
23098
+ },
23099
+ /* ── reading and writing ──────────────────────────────────────────── */
23100
+ fd_read: (fd, iovs, iovsLen, nreadPtr) => this.readInto(fd, iovs, iovsLen, nreadPtr, null),
23101
+ fd_pread: (fd, iovs, iovsLen, offset, nreadPtr) => this.readInto(fd, iovs, iovsLen, nreadPtr, Number(offset)),
23102
+ fd_write: (fd, iovs, iovsLen, nwrittenPtr) => this.writeFrom(fd, iovs, iovsLen, nwrittenPtr, null),
23103
+ fd_pwrite: (fd, iovs, iovsLen, offset, nwrittenPtr) => this.writeFrom(fd, iovs, iovsLen, nwrittenPtr, Number(offset)),
23104
+ fd_seek: (fd, offset, whence, ptr) => {
23105
+ const entry = this.fds.get(fd);
23106
+ if (!entry) return E.BADF;
23107
+ if (entry.kind === "stdout" || entry.kind === "stderr") return E.SPIPE;
23108
+ let next = Number(offset);
23109
+ if (whence === WHENCE_CUR) next += entry.pos;
23110
+ else if (whence === WHENCE_END) next += entry.data.length;
23111
+ if (next < 0) return E.INVAL;
23112
+ entry.pos = next;
23113
+ this.view.setBigUint64(ptr, BigInt(next), true);
23114
+ return E.SUCCESS;
23115
+ },
23116
+ fd_tell: (fd, ptr) => {
23117
+ const entry = this.fds.get(fd);
23118
+ if (!entry) return E.BADF;
23119
+ this.view.setBigUint64(ptr, BigInt(entry.pos), true);
23120
+ return E.SUCCESS;
23121
+ },
23122
+ fd_advise: () => E.SUCCESS,
23123
+ fd_allocate: (fd, offset, len) => {
23124
+ const entry = this.fds.get(fd);
23125
+ if (!entry || entry.kind !== "file") return E.BADF;
23126
+ const end = Number(offset) + Number(len);
23127
+ if (end > entry.data.length) {
23128
+ const next = new Uint8Array(end);
23129
+ next.set(entry.data);
23130
+ entry.data = next;
23131
+ entry.dirty = true;
23132
+ }
23133
+ return this.flush(entry);
23134
+ },
23135
+ fd_readdir: (fd, buf, bufLen, cookie, usedPtr) => {
23136
+ const entry = this.fds.get(fd);
23137
+ if (!entry) return E.BADF;
23138
+ if (entry.kind !== "dir") return E.NOTDIR;
23139
+ let names;
23140
+ try {
23141
+ names = [".", "..", ...vfs.readdir(entry.path, cred)];
23142
+ } catch (e) {
23143
+ return this.errnoOf(e);
23144
+ }
23145
+ const view = this.view;
23146
+ const bytes = this.bytes;
23147
+ let used = 0;
23148
+ for (let i = Number(cookie); i < names.length; i++) {
23149
+ const name = names[i];
23150
+ const encoded = encoder6.encode(name);
23151
+ if (used + SIZEOF_DIRENT + encoded.length > bufLen) break;
23152
+ let type = FT.UNKNOWN;
23153
+ try {
23154
+ const target = name === "." ? entry.path : name === ".." ? dirname(entry.path) : join(entry.path, name);
23155
+ type = this.filetypeOf(vfs.lstat(target).mode);
23156
+ } catch {
23157
+ }
23158
+ const at = buf + used;
23159
+ view.setBigUint64(at, BigInt(i + 1), true);
23160
+ view.setBigUint64(at + 8, 0n, true);
23161
+ view.setUint32(at + 16, encoded.length, true);
23162
+ view.setUint8(at + 20, type);
23163
+ bytes.set(encoded, at + SIZEOF_DIRENT);
23164
+ used += SIZEOF_DIRENT + encoded.length;
23165
+ }
23166
+ view.setUint32(usedPtr, used, true);
23167
+ return E.SUCCESS;
23168
+ },
23169
+ /* ── paths ────────────────────────────────────────────────────────── */
23170
+ path_open: (dirfd, lookupflags, pathPtr, pathLen, oflags, rightsBase, _rightsInheriting, fdflags, fdPtr) => {
23171
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23172
+ if (typeof resolved === "number") return resolved;
23173
+ let path = resolved.path;
23174
+ const wantsWrite = (rightsBase & RIGHT_FD_WRITE) !== 0n || (oflags & (O_CREAT | O_TRUNC)) !== 0;
23175
+ const follow = (lookupflags & LOOKUP_SYMLINK_FOLLOW) !== 0;
23176
+ try {
23177
+ if (follow && vfs.lexists(path)) path = vfs.realpath(path, cred);
23178
+ } catch {
23179
+ }
23180
+ const exists = vfs.lexists(path);
23181
+ if (exists && (oflags & O_EXCL) !== 0 && (oflags & O_CREAT) !== 0) return E.EXIST;
23182
+ if (!exists && (oflags & O_CREAT) === 0) return E.NOENT;
23183
+ try {
23184
+ if (!exists) {
23185
+ if ((oflags & O_DIRECTORY) !== 0) {
23186
+ vfs.mkdir(path, { cred, mode: 493 });
23187
+ } else {
23188
+ vfs.writeFile(path, new Uint8Array(0), { cred, mode: 420 });
23189
+ }
23190
+ }
23191
+ const st = vfs.lstat(path);
23192
+ const isDir = (st.mode & 61440) === S_IFDIR;
23193
+ if ((oflags & O_DIRECTORY) !== 0 && !isDir) return E.NOTDIR;
23194
+ if (isDir && wantsWrite) return E.ISDIR;
23195
+ let data = new Uint8Array(0);
23196
+ if (!isDir) {
23197
+ data = (oflags & O_TRUNC) !== 0 ? new Uint8Array(0) : vfs.readFile(path, cred);
23198
+ if ((oflags & O_TRUNC) !== 0) vfs.writeFile(path, data, { cred });
23199
+ }
23200
+ const fd = this.allocateFd();
23201
+ this.fds.set(fd, {
23202
+ kind: isDir ? "dir" : "file",
23203
+ path,
23204
+ pos: (fdflags & FD_APPEND) !== 0 ? data.length : 0,
23205
+ data,
23206
+ dirty: false,
23207
+ fdflags,
23208
+ /* The requested rights are recorded but not enforced: the
23209
+ * container's own uid/gid permission bits already gated the open,
23210
+ * and layering WASI's capability set on top of them only invents
23211
+ * ENOTCAPABLE failures for modules whose toolchain requests a
23212
+ * narrower set than it then uses. A module may still drop its own
23213
+ * rights through `fd_fdstat_set_rights`, and that is honoured. */
23214
+ rights: RIGHTS_ALL
23215
+ });
23216
+ this.view.setUint32(fdPtr, fd, true);
23217
+ return E.SUCCESS;
23218
+ } catch (e) {
23219
+ return this.errnoOf(e);
23220
+ }
23221
+ },
23222
+ path_filestat_get: (dirfd, flags, pathPtr, pathLen, statPtr) => {
23223
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23224
+ if (typeof resolved === "number") return resolved;
23225
+ return this.writeFilestat(statPtr, resolved.path, (flags & LOOKUP_SYMLINK_FOLLOW) !== 0);
23226
+ },
23227
+ path_filestat_set_times: (dirfd, _flags, pathPtr, pathLen, atim, mtim, _fstflags) => {
23228
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23229
+ if (typeof resolved === "number") return resolved;
23230
+ try {
23231
+ vfs.utimes(resolved.path, Number(atim) / 1e6, Number(mtim) / 1e6, cred);
23232
+ return E.SUCCESS;
23233
+ } catch (e) {
23234
+ return this.errnoOf(e);
23235
+ }
23236
+ },
23237
+ path_create_directory: (dirfd, pathPtr, pathLen) => {
23238
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23239
+ if (typeof resolved === "number") return resolved;
23240
+ try {
23241
+ vfs.mkdir(resolved.path, { cred, mode: 493 });
23242
+ return E.SUCCESS;
23243
+ } catch (e) {
23244
+ return this.errnoOf(e);
23245
+ }
23246
+ },
23247
+ path_remove_directory: (dirfd, pathPtr, pathLen) => {
23248
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23249
+ if (typeof resolved === "number") return resolved;
23250
+ try {
23251
+ vfs.rmdir(resolved.path, cred);
23252
+ return E.SUCCESS;
23253
+ } catch (e) {
23254
+ return this.errnoOf(e);
23255
+ }
23256
+ },
23257
+ path_unlink_file: (dirfd, pathPtr, pathLen) => {
23258
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23259
+ if (typeof resolved === "number") return resolved;
23260
+ try {
23261
+ vfs.unlink(resolved.path, cred);
23262
+ return E.SUCCESS;
23263
+ } catch (e) {
23264
+ return this.errnoOf(e);
23265
+ }
23266
+ },
23267
+ path_rename: (oldFd, oldPtr, oldLen, newFd, newPtr, newLen) => {
23268
+ const from = this.resolveAt(oldFd, this.readString(oldPtr, oldLen));
23269
+ if (typeof from === "number") return from;
23270
+ const to = this.resolveAt(newFd, this.readString(newPtr, newLen));
23271
+ if (typeof to === "number") return to;
23272
+ try {
23273
+ vfs.rename(from.path, to.path, cred);
23274
+ return E.SUCCESS;
23275
+ } catch (e) {
23276
+ return this.errnoOf(e);
23277
+ }
23278
+ },
23279
+ path_symlink: (targetPtr, targetLen, dirfd, pathPtr, pathLen) => {
23280
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23281
+ if (typeof resolved === "number") return resolved;
23282
+ try {
23283
+ vfs.symlink(this.readString(targetPtr, targetLen), resolved.path, cred);
23284
+ return E.SUCCESS;
23285
+ } catch (e) {
23286
+ return this.errnoOf(e);
23287
+ }
23288
+ },
23289
+ path_readlink: (dirfd, pathPtr, pathLen, buf, bufLen, usedPtr) => {
23290
+ const resolved = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
23291
+ if (typeof resolved === "number") return resolved;
23292
+ try {
23293
+ const target = encoder6.encode(vfs.readlink(resolved.path, cred));
23294
+ const size = Math.min(target.length, bufLen);
23295
+ this.bytes.set(target.subarray(0, size), buf);
23296
+ this.view.setUint32(usedPtr, size, true);
23297
+ return E.SUCCESS;
23298
+ } catch (e) {
23299
+ return this.errnoOf(e);
23300
+ }
23301
+ },
23302
+ path_link: (oldFd, _flags, oldPtr, oldLen, newFd, newPtr, newLen) => {
23303
+ const from = this.resolveAt(oldFd, this.readString(oldPtr, oldLen));
23304
+ if (typeof from === "number") return from;
23305
+ const to = this.resolveAt(newFd, this.readString(newPtr, newLen));
23306
+ if (typeof to === "number") return to;
23307
+ try {
23308
+ vfs.link(from.path, to.path, cred);
23309
+ return E.SUCCESS;
23310
+ } catch (e) {
23311
+ return this.errnoOf(e);
23312
+ }
23313
+ },
23314
+ /* ── polling ──────────────────────────────────────────────────────── */
23315
+ /**
23316
+ * Clock subscriptions only, and they resolve immediately.
23317
+ *
23318
+ * Everything here is synchronous, so a genuine wait would deadlock the
23319
+ * whole container rather than yield to anything. Reporting the timer as
23320
+ * already elapsed lets `sleep` and timeout loops complete instead of
23321
+ * hanging; subscriptions on a descriptor report readiness, since stdin is
23322
+ * already buffered and stdout never blocks.
23323
+ */
23324
+ poll_oneoff: (inPtr, outPtr, nsubs, neventsPtr) => {
23325
+ const view = this.view;
23326
+ for (let i = 0; i < nsubs; i++) {
23327
+ const sub = inPtr + i * 48;
23328
+ const userdata = view.getBigUint64(sub, true);
23329
+ const type = view.getUint8(sub + 8);
23330
+ const event = outPtr + i * 32;
23331
+ view.setBigUint64(event, userdata, true);
23332
+ view.setUint16(event + 8, E.SUCCESS, true);
23333
+ view.setUint8(event + 10, type);
23334
+ view.setBigUint64(event + 16, 0n, true);
23335
+ view.setUint16(event + 24, 0, true);
23336
+ }
23337
+ view.setUint32(neventsPtr, nsubs, true);
23338
+ return E.SUCCESS;
23339
+ },
23340
+ /* ── sockets ──────────────────────────────────────────────────────── */
23341
+ sock_accept: () => E.NOTSUP,
23342
+ sock_recv: () => E.NOTSUP,
23343
+ sock_send: () => E.NOTSUP,
23344
+ sock_shutdown: () => E.NOTSUP
23345
+ };
23346
+ }
23347
+ /* ── shared read/write paths ────────────────────────────────────────────── */
23348
+ readInto(fd, iovs, iovsLen, nreadPtr, offset) {
23349
+ const entry = this.fds.get(fd);
23350
+ if (!entry) return E.BADF;
23351
+ if (entry.kind === "dir") return E.ISDIR;
23352
+ if (entry.kind === "stdout" || entry.kind === "stderr") return E.BADF;
23353
+ if ((entry.rights & RIGHT_FD_READ) === 0n) return E.NOTCAPABLE;
23354
+ let position = offset ?? entry.pos;
23355
+ let read = 0;
23356
+ const bytes = this.bytes;
23357
+ for (const iov of this.iovecs(iovs, iovsLen)) {
23358
+ if (position >= entry.data.length) break;
23359
+ const chunk = entry.data.subarray(position, position + iov.len);
23360
+ bytes.set(chunk, iov.base);
23361
+ position += chunk.length;
23362
+ read += chunk.length;
23363
+ if (chunk.length < iov.len) break;
23364
+ }
23365
+ if (offset === null) entry.pos = position;
23366
+ this.view.setUint32(nreadPtr, read, true);
23367
+ return E.SUCCESS;
23368
+ }
23369
+ writeFrom(fd, iovs, iovsLen, nwrittenPtr, offset) {
23370
+ const entry = this.fds.get(fd);
23371
+ if (!entry) return E.BADF;
23372
+ if (entry.kind === "dir") return E.ISDIR;
23373
+ if (entry.kind === "stdin") return E.BADF;
23374
+ if ((entry.rights & RIGHT_FD_WRITE) === 0n) return E.NOTCAPABLE;
23375
+ const chunks = [];
23376
+ let total = 0;
23377
+ const bytes = this.bytes;
23378
+ for (const iov of this.iovecs(iovs, iovsLen)) {
23379
+ chunks.push(bytes.slice(iov.base, iov.base + iov.len));
23380
+ total += iov.len;
23381
+ }
23382
+ if (entry.kind === "stdout" || entry.kind === "stderr") {
23383
+ const sink = entry.kind === "stdout" ? this.opts.stdout : this.opts.stderr;
23384
+ for (const chunk of chunks) sink(chunk);
23385
+ this.view.setUint32(nwrittenPtr, total, true);
23386
+ return E.SUCCESS;
23387
+ }
23388
+ let position = offset ?? ((entry.fdflags & FD_APPEND) !== 0 ? entry.data.length : entry.pos);
23389
+ const end = Math.max(entry.data.length, position + total);
23390
+ if (end > entry.data.length) {
23391
+ const grown = new Uint8Array(end);
23392
+ grown.set(entry.data);
23393
+ entry.data = grown;
23394
+ }
23395
+ for (const chunk of chunks) {
23396
+ entry.data.set(chunk, position);
23397
+ position += chunk.length;
23398
+ }
23399
+ if (offset === null) entry.pos = position;
23400
+ entry.dirty = true;
23401
+ const status = this.flush(entry);
23402
+ if (status !== E.SUCCESS) return status;
23403
+ this.view.setUint32(nwrittenPtr, total, true);
23404
+ return E.SUCCESS;
23405
+ }
23406
+ allocateFd() {
23407
+ while (this.fds.has(this.nextFd)) this.nextFd++;
23408
+ return this.nextFd++;
23409
+ }
23410
+ envPairs() {
23411
+ return Object.entries(this.opts.env).filter(([, value]) => value !== void 0).map(([key, value]) => `${key}=${value}`);
23412
+ }
23413
+ /** Flush and drop every open descriptor. Safe to call twice. */
23414
+ close() {
23415
+ for (const [fd, entry] of this.fds) {
23416
+ if (entry.kind === "file") this.flush(entry);
23417
+ if (fd > 2) this.fds.delete(fd);
23418
+ }
23419
+ }
23420
+ };
23421
+
23422
+ // src/runtime/wasm.ts
23423
+ init_path();
23424
+ var WASM_MAGIC = [0, 97, 115, 109];
23425
+ function isWasmBinary(head2) {
23426
+ return head2.length >= 4 && WASM_MAGIC.every((byte, i) => head2[i] === byte);
23427
+ }
23428
+ function analyse(module) {
23429
+ const imports = WebAssembly.Module.imports(module);
23430
+ const exports = WebAssembly.Module.exports(module).map((e) => e.name);
23431
+ const modules = [...new Set(imports.map((i) => i.module))];
23432
+ const importsMemory = imports.some((i) => i.kind === "memory");
23433
+ const wantsThreads = imports.some(
23434
+ (i) => i.kind === "memory" && i.type?.shared === true
23435
+ );
23436
+ let flavour = "core";
23437
+ if (modules.some((m) => m === "wasi_snapshot_preview1" || m === "wasi_unstable")) flavour = "wasi";
23438
+ else if (modules.some((m) => m.startsWith("wasi:"))) flavour = "wasi-component";
23439
+ else if (modules.includes("gojs") || modules.includes("go")) flavour = "go-js";
23440
+ else if (modules.some((m) => m.startsWith("./") || m.endsWith("_bg.js"))) flavour = "wasm-bindgen";
23441
+ else if (modules.includes("env") || modules.includes("wasi_snapshot_preview1")) flavour = "emscripten";
23442
+ return { flavour, modules, importsMemory, wantsThreads, exports };
23443
+ }
23444
+ var compiled2 = /* @__PURE__ */ new Map();
23445
+ var MAX_CACHED = 16;
23446
+ async function compile(ctx, path) {
23447
+ const st = ctx.vfs.stat(path, { cred: ctx.cred });
23448
+ const hit = compiled2.get(path);
23449
+ if (hit && hit.mtimeMs === st.mtimeMs && hit.size === st.size) return hit.module;
23450
+ const bytes = ctx.vfs.readFile(path, ctx.cred);
23451
+ const buffer = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
23452
+ const module = await WebAssembly.compile(buffer);
23453
+ if (compiled2.size >= MAX_CACHED) {
23454
+ const oldest = compiled2.keys().next().value;
23455
+ if (oldest !== void 0) compiled2.delete(oldest);
23456
+ }
23457
+ compiled2.set(path, { mtimeMs: st.mtimeMs, size: st.size, module });
23458
+ return module;
23459
+ }
23460
+ async function runWasi(ctx, path, argv0) {
23461
+ let module;
23462
+ try {
23463
+ module = await compile(ctx, path);
23464
+ } catch (e) {
23465
+ ctx.warn(`${path}: not a valid WebAssembly module (${e instanceof Error ? e.message : String(e)})`);
23466
+ return 126;
23467
+ }
23468
+ const info = analyse(module);
23469
+ if (info.flavour !== "wasi") {
23470
+ for (const line of explainFlavour(info, path, findLoader(ctx, path))) ctx.stderr.write(line + "\n");
23471
+ return 126;
23472
+ }
23473
+ if (info.wantsThreads) {
23474
+ ctx.warn("this module was built with threads (shared memory), which this container cannot provide");
23475
+ return 126;
23476
+ }
23477
+ let stdin = new Uint8Array(0);
23478
+ if (!ctx.stdin.isTTY && !ctx.stdin.interactive) {
23479
+ stdin = await ctx.stdin.readAll();
23480
+ }
23481
+ const wasi = new WasiPreview1({
23482
+ vfs: ctx.vfs,
23483
+ cred: ctx.cred,
23484
+ args: [argv0 ?? basename(path), ...ctx.args],
23485
+ env: ctx.env,
23486
+ cwd: ctx.cwd,
23487
+ stdin,
23488
+ stdout: (bytes) => ctx.write(bytes),
23489
+ stderr: (bytes) => ctx.stderr.write(bytes),
23490
+ now: ctx.kernel.now
23491
+ });
23492
+ let instance;
23493
+ try {
23494
+ instance = await WebAssembly.instantiate(module, wasi.imports);
23495
+ } catch (e) {
23496
+ ctx.warn(`cannot start: ${e instanceof Error ? e.message : String(e)}`);
23497
+ return 126;
23498
+ }
23499
+ try {
23500
+ wasi.bind(instance);
23501
+ } catch (e) {
23502
+ ctx.warn(e instanceof Error ? e.message : String(e));
23503
+ return 126;
23504
+ }
23505
+ const start = instance.exports._start ?? instance.exports._initialize;
23506
+ if (typeof start !== "function") {
23507
+ ctx.warn("module exports neither _start nor _initialize; it is a library, not a command");
23508
+ return 126;
23509
+ }
23510
+ try {
23511
+ start();
23512
+ return wasi.exited ?? 0;
23513
+ } catch (e) {
23514
+ if (e instanceof WasiExit) return e.code;
23515
+ ctx.warn(e instanceof Error ? e.message : String(e));
23516
+ return 128 + 6;
23517
+ } finally {
23518
+ wasi.close();
23519
+ }
23520
+ }
23521
+ function findLoader(ctx, path) {
23522
+ const stem = join(dirname(path), basename(path, ".wasm")).replace(/_bg$/, "");
23523
+ for (const candidate of [`${stem}.js`, `${stem}.mjs`, `${stem}.cjs`]) {
23524
+ if (ctx.vfs.lexists(candidate)) return candidate;
23525
+ }
23526
+ return null;
23527
+ }
23528
+ function explainFlavour(info, path, loader) {
23529
+ const name = basename(path);
23530
+ if (loader) {
23531
+ return [
23532
+ `${name}: this module is loaded by a JavaScript harness, not executed directly.`,
23533
+ "It imports host functions that only that harness provides. Run it instead:",
23534
+ ` node ${loader}`
23535
+ ];
23536
+ }
23537
+ switch (info.flavour) {
23538
+ case "wasi-component":
23539
+ return [
23540
+ `${name}: this is a WebAssembly component (WASI preview 2), not a core module.`,
23541
+ "Components cannot be instantiated directly. Transpile it to a core module first:",
23542
+ " npx @bytecodealliance/jco transpile " + name + " -o out"
23543
+ ];
23544
+ case "emscripten":
23545
+ return [
23546
+ `${name}: this is an Emscripten build, which is only half a program.`,
23547
+ "It needs the JavaScript loader generated alongside it. Run that instead:",
23548
+ ` node ${basename(path, ".wasm")}.js`
23549
+ ];
23550
+ case "wasm-bindgen":
23551
+ return [
23552
+ `${name}: this is a wasm-bindgen build; its imports come from a JS glue module.`,
23553
+ "Load it from Node rather than executing it:",
23554
+ ` node -e "import('./${basename(path, "_bg.wasm")}.js')"`
23555
+ ];
23556
+ case "go-js":
23557
+ return [
23558
+ `${name}: this was built with GOOS=js, which needs Go's wasm_exec.js harness.`,
23559
+ "Rebuild it for WASI instead, and it will run here directly:",
23560
+ " GOOS=wasip1 GOARCH=wasm go build -o " + name
23561
+ ];
23562
+ default:
23563
+ return [
23564
+ `${name}: a core WebAssembly module with no WASI imports.`,
23565
+ "There is no entry point to call and no way to reach stdin, stdout or the",
23566
+ "filesystem. Build it for wasm32-wasi to run it as a command."
23567
+ ];
23568
+ }
23569
+ }
23570
+ var wasmFormat = {
23571
+ name: "wasm",
23572
+ priority: 10,
23573
+ matches: (head2) => isWasmBinary(head2),
23574
+ describe: () => "WebAssembly (wasm) binary module",
23575
+ run: (ctx, path) => runWasi(ctx, path, ctx.name)
23576
+ };
23577
+ var wasm = defineCommand({
23578
+ name: "wasm",
23579
+ path: "/usr/bin/wasm",
23580
+ summary: "run and inspect WebAssembly modules",
23581
+ usage: "wasm run <module.wasm> [args...] | wasm info <module.wasm>",
23582
+ manual: `Runs a WASI command module against the container's filesystem, or
23583
+ reports what a module was built for.
23584
+
23585
+ Any file starting with the WebAssembly magic bytes is also executable
23586
+ directly, so 'chmod +x tool.wasm && ./tool.wasm' does the same thing as
23587
+ 'wasm run tool.wasm'.`,
23588
+ async run(ctx) {
23589
+ const [subcommand, target, ...rest] = ctx.args;
23590
+ if (subcommand === void 0 || subcommand === "--help" || subcommand === "-h") {
23591
+ ctx.line("Usage: wasm run <module.wasm> [args...]");
23592
+ ctx.line(" wasm info <module.wasm>");
23593
+ return subcommand === void 0 ? 2 : 0;
23594
+ }
23595
+ if (target === void 0) return ctx.fail(`${subcommand}: a module path is required`, 2);
23596
+ const path = ctx.path(target);
23597
+ if (!ctx.vfs.lexists(path)) return ctx.fail(`${target}: No such file or directory`, 1);
23598
+ if (subcommand === "run") {
23599
+ return await runWasi({ ...ctx, args: rest, argv: [basename(path), ...rest] }, path, basename(path));
23600
+ }
23601
+ if (subcommand === "info") {
23602
+ let module;
23603
+ try {
23604
+ module = await compile(ctx, path);
23605
+ } catch (e) {
23606
+ return ctx.fail(`${target}: ${e instanceof Error ? e.message : String(e)}`, 1);
23607
+ }
23608
+ const info = analyse(module);
23609
+ const runnable = info.flavour === "wasi" && !info.wantsThreads;
23610
+ ctx.line(`file: ${path}`);
23611
+ ctx.line(`flavour: ${info.flavour}`);
23612
+ ctx.line(`imports: ${info.modules.join(", ") || "(none)"}`);
23613
+ ctx.line(`threads: ${info.wantsThreads ? "yes (unsupported)" : "no"}`);
23614
+ ctx.line(`entry: ${info.exports.includes("_start") ? "_start" : info.exports.includes("_initialize") ? "_initialize (reactor)" : "none"}`);
23615
+ ctx.line(`runnable: ${runnable ? "yes" : "no"}`);
23616
+ if (!runnable) {
23617
+ ctx.line("");
23618
+ for (const line of explainFlavour(info, path, findLoader(ctx, path))) ctx.line(line);
23619
+ }
23620
+ return 0;
23621
+ }
23622
+ return ctx.fail(`unknown subcommand: ${subcommand}`, 2);
23623
+ }
23624
+ });
23625
+ function wasmCommands() {
23626
+ return [wasm];
23627
+ }
23628
+
23629
+ // src/runtime/native-format.ts
23630
+ init_path();
23631
+ var ELF_MACHINES = {
23632
+ 2: "SPARC",
23633
+ 3: "x86",
23634
+ 8: "MIPS",
23635
+ 20: "PowerPC",
23636
+ 21: "PowerPC 64-bit",
23637
+ 22: "S/390",
23638
+ 40: "ARM",
23639
+ 42: "SuperH",
23640
+ 50: "IA-64",
23641
+ 62: "x86-64",
23642
+ 183: "AArch64",
23643
+ 243: "RISC-V"
23644
+ };
23645
+ var ELF_TYPES = {
23646
+ 1: "relocatable",
23647
+ 2: "executable",
23648
+ 3: "shared object",
23649
+ 4: "core dump"
23650
+ };
23651
+ function describeElf(head2) {
23652
+ const bits = head2[4] === 2 ? "64-bit" : "32-bit";
23653
+ const endian = head2[5] === 2 ? "MSB" : "LSB";
23654
+ if (head2.length < 20) return `ELF ${bits} ${endian} object`;
23655
+ const view = new DataView(head2.buffer, head2.byteOffset, head2.byteLength);
23656
+ const little = head2[5] !== 2;
23657
+ const type = ELF_TYPES[view.getUint16(16, little)] ?? "object";
23658
+ const machine = view.getUint16(18, little);
23659
+ return `ELF ${bits} ${endian} ${type}, ${ELF_MACHINES[machine] ?? `machine 0x${machine.toString(16)}`}`;
23660
+ }
23661
+ var SUGGEST_WASM = [
23662
+ "Nothing in this container can execute machine code \u2014 there is no CPU here to",
23663
+ "run it on, only JavaScript and WebAssembly.",
23664
+ "",
23665
+ "If the tool has a WebAssembly build, it will run here as-is: drop the .wasm",
23666
+ "file in /usr/local/bin, chmod +x it, and call it by name."
23667
+ ];
23668
+ var elfFormat = {
23669
+ name: "elf",
23670
+ priority: 20,
23671
+ matches: (head2) => head2.length >= 4 && head2[0] === 127 && head2[1] === 69 && head2[2] === 76 && head2[3] === 70,
23672
+ describe: describeElf,
23673
+ explain: (head2, path) => [`${basename(path)}: ${describeElf(head2)}.`, ...SUGGEST_WASM]
23674
+ };
23675
+ var machoFormat = {
23676
+ name: "mach-o",
23677
+ priority: 20,
23678
+ matches: (head2) => {
23679
+ if (head2.length < 4) return false;
23680
+ const magic = new DataView(head2.buffer, head2.byteOffset, head2.byteLength).getUint32(0, false);
23681
+ return magic === 4277009102 || magic === 4277009103 || magic === 3472551422 || magic === 3489328638 || magic === 3405691582;
23682
+ },
23683
+ describe: () => "Mach-O executable (macOS)",
23684
+ explain: (_head, path) => [`${basename(path)}: Mach-O executable (macOS native binary).`, ...SUGGEST_WASM]
23685
+ };
23686
+ var peFormat = {
23687
+ name: "pe",
23688
+ priority: 25,
23689
+ // `MZ` alone is weak, so require the DOS stub text that every real PE carries.
23690
+ matches: (head2) => head2.length >= 64 && head2[0] === 77 && head2[1] === 90 && new TextDecoder().decode(head2.subarray(0, 128)).includes("This program cannot be run in DOS mode"),
23691
+ describe: () => "PE32+ executable (Windows)",
23692
+ explain: (_head, path) => [`${basename(path)}: PE executable (Windows native binary).`, ...SUGGEST_WASM]
23693
+ };
23694
+ function nativeFormats() {
23695
+ return [elfFormat, machoFormat, peFormat];
23696
+ }
22633
23697
  var DECLARES_EXPORTS = /(?:^|[\s;{)])(?:var|let|const|function|class)\s+exports\b/;
22634
23698
  var NAME = "exports";
22635
23699
  function renameShadowedExports(source) {
@@ -22849,6 +23913,143 @@ var NOTHING = [];
22849
23913
  var PROPERTY = ["property", "key"];
22850
23914
  var LABEL = ["label"];
22851
23915
 
23916
+ // src/pkg/install.ts
23917
+ init_path();
23918
+ var GLOBAL_PREFIX = "/usr/local";
23919
+ var GLOBAL_MODULES = `${GLOBAL_PREFIX}/lib/node_modules`;
23920
+ var GLOBAL_BIN = `${GLOBAL_PREFIX}/bin`;
23921
+ function readManifestAt(ctx, dir3) {
23922
+ try {
23923
+ return JSON.parse(ctx.vfs.readText(join(dir3, "package.json"), ctx.cred));
23924
+ } catch {
23925
+ return null;
23926
+ }
23927
+ }
23928
+ function binEntries(manifest, packageName) {
23929
+ if (typeof manifest.bin === "string") return { [basename(packageName)]: manifest.bin };
23930
+ if (manifest.bin && typeof manifest.bin === "object") return { ...manifest.bin };
23931
+ return {};
23932
+ }
23933
+ function linkPackageBinaries(ctx, packageDir, packageName, binDir) {
23934
+ const manifest = readManifestAt(ctx, packageDir);
23935
+ if (!manifest) return [];
23936
+ const linked = [];
23937
+ if (!ctx.vfs.lexists(binDir)) ctx.vfs.mkdir(binDir, { recursive: true, mode: 493 });
23938
+ for (const [name, relative2] of Object.entries(binEntries(manifest, packageName))) {
23939
+ const target = resolve(packageDir, relative2);
23940
+ if (!ctx.vfs.lexists(target)) continue;
23941
+ let script;
23942
+ try {
23943
+ script = isJavaScriptEntry(ctx, target) ? `#!/bin/sh
23944
+ exec node ${quote(target)} "$@"
23945
+ ` : `#!/bin/sh
23946
+ exec ${quote(target)} "$@"
23947
+ `;
23948
+ } catch {
23949
+ continue;
23950
+ }
23951
+ const link = join(binDir, name);
23952
+ ctx.vfs.writeFile(link, script, { privileged: true, mode: 493 });
23953
+ ctx.vfs.chmod(link, 493);
23954
+ try {
23955
+ ctx.vfs.chmod(target, 493, ctx.cred);
23956
+ } catch {
23957
+ }
23958
+ linked.push(name);
23959
+ }
23960
+ return linked;
23961
+ }
23962
+ function quote(value) {
23963
+ return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
23964
+ }
23965
+ function isJavaScriptEntry(ctx, target) {
23966
+ const head2 = ctx.vfs.readFile(ctx.vfs.realpath(target, ctx.cred), ctx.cred).subarray(0, 256);
23967
+ if (isWasmBinary(head2)) return false;
23968
+ const text = new TextDecoder().decode(head2);
23969
+ if (text.startsWith("#!")) {
23970
+ return false;
23971
+ }
23972
+ if (head2.includes(0)) return false;
23973
+ const ext = extname(target);
23974
+ if (JS_EXTENSIONS.has(ext) && ext !== "") return true;
23975
+ return !/^\s*(echo|set|export|if|exec)\s/.test(text);
23976
+ }
23977
+ var LIFECYCLE = ["preinstall", "install", "postinstall"];
23978
+ async function runLifecycleScripts(ctx, packageDir, packageName, opts = {}) {
23979
+ const manifest = readManifestAt(ctx, packageDir);
23980
+ const scripts = manifest?.scripts;
23981
+ if (!scripts) return;
23982
+ for (const hook of LIFECYCLE) {
23983
+ const script = scripts[hook];
23984
+ if (!script) continue;
23985
+ if (!opts.quiet) ctx.stderr.write(`
23986
+ > ${packageName} ${hook}
23987
+ > ${script}
23988
+ `);
23989
+ const binDir = join(packageDir, "node_modules", ".bin");
23990
+ const result = await ctx.kernel.run(["/bin/sh", "-c", script], {
23991
+ cwd: packageDir,
23992
+ cred: ctx.cred,
23993
+ ppid: ctx.proc.pid,
23994
+ env: {
23995
+ ...ctx.env,
23996
+ PATH: `${binDir}:${GLOBAL_BIN}:${ctx.env.PATH ?? ""}`,
23997
+ npm_lifecycle_event: hook,
23998
+ npm_lifecycle_script: script,
23999
+ npm_package_name: manifest.name ?? packageName,
24000
+ npm_package_version: manifest.version ?? "",
24001
+ INIT_CWD: ctx.cwd
24002
+ },
24003
+ // Hooks that wait on something that will never arrive are common enough
24004
+ // to need a bound; without one a single package hangs the install.
24005
+ timeoutMs: opts.timeoutMs ?? 6e4
24006
+ });
24007
+ if (result.exitCode !== 0) {
24008
+ ctx.stderr.write(`npm warn ${packageName}: ${hook} script failed (exit ${result.exitCode})
24009
+ `);
24010
+ const detail = (result.stderr || result.stdout).trim().split("\n").slice(-3);
24011
+ for (const line of detail) if (line) ctx.stderr.write(`npm warn ${line}
24012
+ `);
24013
+ return;
24014
+ }
24015
+ if (!opts.quiet && result.stdout) ctx.stderr.write(result.stdout);
24016
+ }
24017
+ }
24018
+ var JS_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ""]);
24019
+ function inspectPackage(ctx, packageDir, packageName) {
24020
+ const manifest = readManifestAt(ctx, packageDir);
24021
+ if (!manifest) return { verdict: "empty", lines: [`${packageName}: installed, but has no package.json`] };
24022
+ const bins = binEntries(manifest, packageName);
24023
+ if (Object.keys(bins).length === 0) {
24024
+ return { verdict: "ok", lines: [] };
24025
+ }
24026
+ const nativeBins = Object.entries(bins).filter(([, relative2]) => !JS_EXTENSIONS.has(extname(relative2)));
24027
+ const platformDeps = Object.keys(manifest.optionalDependencies ?? {}).filter(
24028
+ (dep) => /-(linux|darwin|win32|windows|freebsd)(-|$)|-(x64|arm64|ia32)(-|$)/.test(dep)
24029
+ );
24030
+ if (nativeBins.length === 0 && platformDeps.length === 0) return { verdict: "ok", lines: [] };
24031
+ const lines = [];
24032
+ const label = manifest.name ?? packageName;
24033
+ if (nativeBins.length > 0) {
24034
+ const [name, relative2] = nativeBins[0];
24035
+ lines.push(`${label}: '${name}' points at ${relative2}, which is a compiled executable, not JavaScript.`);
24036
+ } else {
24037
+ lines.push(`${label}: ships its executable as a platform-specific package, not as JavaScript.`);
24038
+ }
24039
+ if (platformDeps.length > 0) {
24040
+ const shown = platformDeps.slice(0, 3).join(", ");
24041
+ lines.push(
24042
+ `Its real binary comes from ${platformDeps.length} platform packages (${shown}${platformDeps.length > 3 ? ", \u2026" : ""}),`
24043
+ );
24044
+ lines.push("each of which is native machine code for one OS and CPU.");
24045
+ }
24046
+ lines.push("");
24047
+ lines.push("This container has no CPU to run machine code on \u2014 only JavaScript and");
24048
+ lines.push("WebAssembly. The package is installed and its files are on disk, but the");
24049
+ lines.push("command it provides cannot start.");
24050
+ return { verdict: "native", lines };
24051
+ }
24052
+
22852
24053
  // src/pkg/index.ts
22853
24054
  init_path();
22854
24055
  var NPM_VERSION = "10.9.0";
@@ -22873,6 +24074,9 @@ function findProjectRoot(ctx) {
22873
24074
  function writeManifest(ctx, path, manifest) {
22874
24075
  ctx.vfs.writeFile(path, JSON.stringify(manifest, null, 2) + "\n", { cred: ctx.cred, mode: 420 });
22875
24076
  }
24077
+ function isGlobal(args) {
24078
+ return args.some((a) => a === "-g" || a === "--global" || a === "--location=global");
24079
+ }
22876
24080
  function splitPackageSpec(spec) {
22877
24081
  const at = spec.lastIndexOf("@");
22878
24082
  if (at <= 0) return { name: spec };
@@ -22905,19 +24109,93 @@ async function installPackages(ctx, specs, opts) {
22905
24109
  note(`added ${name}${version ? `@${version}` : ""}`);
22906
24110
  await installer.install(name, version, {
22907
24111
  onProgress,
22908
- persist: opts.save !== false,
24112
+ // A global install belongs to the prefix, not to whatever project
24113
+ // happens to be in the working directory, so nothing is recorded in
24114
+ // a manifest — which is also what npm does.
24115
+ persist: opts.global ? false : opts.save !== false,
22909
24116
  persistDev: opts.dev === true
22910
24117
  });
22911
24118
  }
22912
24119
  }
22913
24120
  normalizeBinDirectories(ctx, opts.cwd);
22914
24121
  normalizeEsmExports(ctx, opts.cwd);
24122
+ if (opts.global) {
24123
+ return await linkGlobalPackages(ctx, opts.cwd, specs, opts.quiet === true, opts.ignoreScripts !== true);
24124
+ }
24125
+ if (opts.ignoreScripts !== true) await runProjectLifecycleScripts(ctx, opts.cwd, specs, opts.quiet === true);
22915
24126
  return 0;
22916
24127
  } catch (e) {
22917
24128
  ctx.warn(`npm error ${e instanceof Error ? e.message : String(e)}`);
22918
24129
  return 1;
22919
24130
  }
22920
24131
  }
24132
+ async function runProjectLifecycleScripts(ctx, root, specs, quiet) {
24133
+ for (const spec of specs) {
24134
+ const { name } = splitPackageSpec(spec);
24135
+ const dir3 = join(root, "node_modules", name);
24136
+ if (ctx.vfs.lexists(dir3)) await runLifecycleScripts(ctx, dir3, name, { quiet });
24137
+ }
24138
+ normalizeBinDirectories(ctx, root);
24139
+ }
24140
+ async function linkGlobalPackages(ctx, stagingRoot, specs, quiet, runScripts) {
24141
+ const staged = join(stagingRoot, "node_modules");
24142
+ if (!ctx.vfs.lexists(staged)) return 0;
24143
+ if (!ctx.vfs.lexists(GLOBAL_MODULES)) ctx.vfs.mkdir(GLOBAL_MODULES, { recursive: true, mode: 493 });
24144
+ for (const entry of ctx.vfs.readdir(staged, ctx.cred)) {
24145
+ if (entry === ".bin") continue;
24146
+ const from = join(staged, entry);
24147
+ const to = join(GLOBAL_MODULES, entry);
24148
+ try {
24149
+ if (entry.startsWith("@")) {
24150
+ if (!ctx.vfs.lexists(to)) ctx.vfs.mkdir(to, { recursive: true, mode: 493 });
24151
+ for (const scoped of ctx.vfs.readdir(from, ctx.cred)) {
24152
+ const target = join(to, scoped);
24153
+ if (ctx.vfs.lexists(target)) ctx.vfs.rmrf(target, ctx.cred);
24154
+ ctx.vfs.copyTree(join(from, scoped), target, ctx.cred);
24155
+ }
24156
+ } else {
24157
+ if (ctx.vfs.lexists(to)) ctx.vfs.rmrf(to, ctx.cred);
24158
+ ctx.vfs.copyTree(from, to, ctx.cred);
24159
+ }
24160
+ } catch (e) {
24161
+ ctx.warn(`npm warn could not install ${entry} globally: ${e instanceof Error ? e.message : String(e)}`);
24162
+ }
24163
+ }
24164
+ ctx.vfs.rmrf(staged, ctx.cred);
24165
+ for (const leftover of ["package.json", "package-lock.json"]) {
24166
+ const path = join(stagingRoot, leftover);
24167
+ if (stagingRoot === "/" && ctx.vfs.lexists(path)) {
24168
+ try {
24169
+ ctx.vfs.unlink(path, ctx.cred);
24170
+ } catch {
24171
+ }
24172
+ }
24173
+ }
24174
+ let status = 0;
24175
+ for (const spec of specs) {
24176
+ const { name } = splitPackageSpec(spec);
24177
+ const dir3 = join(GLOBAL_MODULES, name);
24178
+ if (!ctx.vfs.lexists(dir3)) continue;
24179
+ if (runScripts) await runLifecycleScripts(ctx, dir3, name, { quiet });
24180
+ const linked = linkPackageBinaries(ctx, dir3, name, GLOBAL_BIN);
24181
+ const report = inspectPackage(ctx, dir3, name);
24182
+ if (report.verdict !== "ok") {
24183
+ ctx.stderr.write("\n");
24184
+ for (const line of report.lines) ctx.stderr.write(`npm warn ${line}
24185
+ `);
24186
+ status = report.verdict === "native" ? 1 : status;
24187
+ continue;
24188
+ }
24189
+ if (!quiet) {
24190
+ if (linked.length > 0) {
24191
+ for (const bin of linked) ctx.line(`${join(GLOBAL_BIN, bin)} -> ${join(dir3, "")}`);
24192
+ } else {
24193
+ ctx.line(`${name} installed to ${GLOBAL_MODULES} (it declares no commands)`);
24194
+ }
24195
+ }
24196
+ }
24197
+ return status;
24198
+ }
22921
24199
  var npm = defineCommand({
22922
24200
  name: "npm",
22923
24201
  path: "/usr/bin/npm",
@@ -22968,17 +24246,56 @@ var npm = defineCommand({
22968
24246
  { long: "production" },
22969
24247
  { long: "omit", arg: true },
22970
24248
  { long: "legacy-peer-deps" },
24249
+ { long: "ignore-scripts" },
22971
24250
  { short: "f", long: "force" }
22972
24251
  ], { allowUnknown: true });
24252
+ if (args.has("global")) {
24253
+ if (args.positional.length === 0) {
24254
+ ctx.warn("npm error a package name is required for a global install");
24255
+ return 1;
24256
+ }
24257
+ const staging = `/var/tmp/.npm-global-${ctx.proc.pid}`;
24258
+ ctx.vfs.mkdir(staging, { recursive: true, mode: 493 });
24259
+ try {
24260
+ return await installPackages(ctx, args.positional, {
24261
+ cwd: staging,
24262
+ save: false,
24263
+ global: true,
24264
+ ignoreScripts: args.has("ignore-scripts")
24265
+ });
24266
+ } finally {
24267
+ try {
24268
+ ctx.vfs.rmrf(staging, ctx.cred);
24269
+ } catch {
24270
+ }
24271
+ }
24272
+ }
22973
24273
  return await installPackages(ctx, args.positional, {
22974
24274
  dev: args.has("save-dev"),
22975
24275
  save: !args.has("no-save"),
22976
- cwd: root
24276
+ cwd: root,
24277
+ ignoreScripts: args.has("ignore-scripts")
22977
24278
  });
22978
24279
  }
22979
24280
  case "uninstall":
22980
24281
  case "remove":
22981
24282
  case "rm": {
24283
+ if (isGlobal(rest)) {
24284
+ for (const name of rest.filter((a) => !a.startsWith("-"))) {
24285
+ const dir3 = join(GLOBAL_MODULES, name);
24286
+ if (!ctx.vfs.lexists(dir3)) {
24287
+ ctx.warn(`npm warn ${name} is not installed globally`);
24288
+ continue;
24289
+ }
24290
+ for (const bin of Object.keys(binEntries(readManifestAt(ctx, dir3) ?? {}, name))) {
24291
+ const link = join(GLOBAL_BIN, bin);
24292
+ if (ctx.vfs.lexists(link)) ctx.vfs.rmrf(link, ctx.cred);
24293
+ }
24294
+ ctx.vfs.rmrf(dir3, ctx.cred);
24295
+ ctx.line(`removed ${name}`);
24296
+ }
24297
+ return 0;
24298
+ }
22982
24299
  const found = readManifest(ctx, root);
22983
24300
  for (const name of rest.filter((a) => !a.startsWith("-"))) {
22984
24301
  const target = join(root, "node_modules", name);
@@ -22994,6 +24311,16 @@ var npm = defineCommand({
22994
24311
  }
22995
24312
  case "ls":
22996
24313
  case "list": {
24314
+ if (isGlobal(rest)) {
24315
+ ctx.line(`${GLOBAL_PREFIX}/lib`);
24316
+ if (!ctx.vfs.lexists(GLOBAL_MODULES)) return 0;
24317
+ const names2 = ctx.vfs.readdir(GLOBAL_MODULES, ctx.cred).filter((n) => !n.startsWith("."));
24318
+ names2.forEach((name, idx) => {
24319
+ const manifest = readManifestAt(ctx, join(GLOBAL_MODULES, name));
24320
+ ctx.line(`${idx === names2.length - 1 ? "\u2514\u2500\u2500" : "\u251C\u2500\u2500"} ${name}@${manifest?.version ?? ""}`);
24321
+ });
24322
+ return 0;
24323
+ }
22997
24324
  const found = readManifest(ctx, root);
22998
24325
  ctx.line(`${found?.manifest.name ?? basename(root)}@${found?.manifest.version ?? "1.0.0"} ${root}`);
22999
24326
  const modules = join(root, "node_modules");
@@ -23041,13 +24368,49 @@ var npm = defineCommand({
23041
24368
  ctx.line("");
23042
24369
  return await runScript(ctx, root, script, scriptArgs, found.manifest);
23043
24370
  }
24371
+ /* Re-run install hooks and rebuild the `$PATH` shims for packages that
24372
+ * are already on disk. Useful here for the same reason it is useful on a
24373
+ * real system — a package whose entry point was generated, replaced or
24374
+ * repaired after installation needs its links redone — and it works
24375
+ * offline, since nothing is fetched. */
24376
+ case "rebuild": {
24377
+ const global = isGlobal(rest);
24378
+ const modulesDir = global ? GLOBAL_MODULES : join(root, "node_modules");
24379
+ const binDir = global ? GLOBAL_BIN : join(root, "node_modules", ".bin");
24380
+ if (!ctx.vfs.lexists(modulesDir)) return ctx.fail(`nothing installed in ${modulesDir}`, 1);
24381
+ const named = rest.filter((a) => !a.startsWith("-"));
24382
+ const targets = named.length > 0 ? named : ctx.vfs.readdir(modulesDir, ctx.cred).filter((n) => !n.startsWith("."));
24383
+ let status = 0;
24384
+ for (const name of targets) {
24385
+ const dir3 = join(modulesDir, name);
24386
+ if (!ctx.vfs.lexists(dir3)) {
24387
+ ctx.warn(`npm warn ${name} is not installed`);
24388
+ status = 1;
24389
+ continue;
24390
+ }
24391
+ if (!rest.includes("--ignore-scripts")) await runLifecycleScripts(ctx, dir3, name, { quiet: true });
24392
+ const linked = linkPackageBinaries(ctx, dir3, name, binDir);
24393
+ const report = inspectPackage(ctx, dir3, name);
24394
+ if (report.verdict !== "ok") {
24395
+ for (const line of report.lines) ctx.stderr.write(`npm warn ${line}
24396
+ `);
24397
+ status = 1;
24398
+ continue;
24399
+ }
24400
+ for (const bin of linked) ctx.line(`${join(binDir, bin)} -> ${dir3}`);
24401
+ }
24402
+ return status;
24403
+ }
23044
24404
  case "exec":
23045
24405
  return await runScript(ctx, root, rest.join(" "), [], readManifest(ctx, root)?.manifest ?? {});
23046
24406
  case "root":
23047
- ctx.line(join(root, "node_modules"));
24407
+ ctx.line(isGlobal(rest) ? GLOBAL_MODULES : join(root, "node_modules"));
23048
24408
  return 0;
23049
24409
  case "prefix":
23050
- ctx.line(root);
24410
+ ctx.line(isGlobal(rest) ? GLOBAL_PREFIX : root);
24411
+ return 0;
24412
+ case "bin":
24413
+ ctx.line(isGlobal(rest) ? GLOBAL_BIN : join(root, "node_modules", ".bin"));
23051
24414
  return 0;
23052
24415
  case "config":
23053
24416
  if (rest[0] === "get") {
@@ -23090,7 +24453,7 @@ async function runScript(ctx, root, script, extraArgs, manifest) {
23090
24453
  npm_config_user_agent: `npm/${NPM_VERSION} node/${NODE_VERSION} linux x64`,
23091
24454
  INIT_CWD: ctx.cwd
23092
24455
  };
23093
- const commandLine = extraArgs.length ? `${script} ${extraArgs.map(quote).join(" ")}` : script;
24456
+ const commandLine = extraArgs.length ? `${script} ${extraArgs.map(quote2).join(" ")}` : script;
23094
24457
  return await ctx.kernel.spawn(["/bin/sh", "-c", commandLine], {
23095
24458
  cwd: root,
23096
24459
  env: env2,
@@ -23101,7 +24464,7 @@ async function runScript(ctx, root, script, extraArgs, manifest) {
23101
24464
  stderr: ctx.stderr
23102
24465
  }).wait();
23103
24466
  }
23104
- function quote(value) {
24467
+ function quote2(value) {
23105
24468
  return /^[A-Za-z0-9_@%+=:,./-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
23106
24469
  }
23107
24470
  function normalizeBinDirectories(ctx, root) {
@@ -23457,11 +24820,16 @@ function allCommands() {
23457
24820
  ...nodeCommands(),
23458
24821
  ...pythonCommands(),
23459
24822
  ...ffmpegCommands(),
24823
+ ...wasmCommands(),
23460
24824
  ...packageCommands()
23461
24825
  ];
23462
24826
  }
24827
+ function allFormats() {
24828
+ return [wasmFormat, ...nativeFormats()];
24829
+ }
23463
24830
  function installUserland(kernel) {
23464
24831
  kernel.installCommands(allCommands());
24832
+ kernel.formats.registerAll(allFormats());
23465
24833
  const vfs = kernel.vfs;
23466
24834
  if (!vfs.lexists("/usr/bin/reset")) {
23467
24835
  vfs.writeFile("/usr/bin/reset", "#!/bin/sh\nprintf '\\033c'\n", { privileged: true, mode: 493 });
@@ -23606,15 +24974,15 @@ var Session = class {
23606
24974
  async run(command, opts = {}) {
23607
24975
  if (this.closed) throw new Error("session is closed");
23608
24976
  const combined = [];
23609
- const decoder7 = new TextDecoder();
24977
+ const decoder8 = new TextDecoder();
23610
24978
  const stdout = new BufferSink((chunk) => {
23611
- const text = decoder7.decode(chunk, { stream: true });
24979
+ const text = decoder8.decode(chunk, { stream: true });
23612
24980
  combined.push(text);
23613
24981
  opts.onStdout?.(text);
23614
24982
  this.hooks.onStdout?.(text);
23615
24983
  });
23616
24984
  const stderr = new BufferSink((chunk) => {
23617
- const text = decoder7.decode(chunk, { stream: true });
24985
+ const text = decoder8.decode(chunk, { stream: true });
23618
24986
  combined.push(text);
23619
24987
  opts.onStderr?.(text);
23620
24988
  this.hooks.onStderr?.(text);
@@ -23950,15 +25318,15 @@ var Container = class _Container {
23950
25318
  }
23951
25319
  makeStdio(opts) {
23952
25320
  const combined = [];
23953
- const decoder7 = new TextDecoder();
25321
+ const decoder8 = new TextDecoder();
23954
25322
  const stdout = new BufferSink((chunk) => {
23955
- const text = decoder7.decode(chunk, { stream: true });
25323
+ const text = decoder8.decode(chunk, { stream: true });
23956
25324
  combined.push(text);
23957
25325
  opts.onStdout?.(text);
23958
25326
  this.hooks.onStdout?.(text);
23959
25327
  });
23960
25328
  const stderr = new BufferSink((chunk) => {
23961
- const text = decoder7.decode(chunk, { stream: true });
25329
+ const text = decoder8.decode(chunk, { stream: true });
23962
25330
  combined.push(text);
23963
25331
  opts.onStderr?.(text);
23964
25332
  this.hooks.onStderr?.(text);