sandboxedjs 0.1.34 → 0.1.36

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
@@ -17,6 +17,10 @@ import timersModule from 'timers-browserify';
17
17
  import legacyUrl from 'url/url.js';
18
18
  import { sha1 as sha1$1, md5 } from '@noble/hashes/legacy';
19
19
  import { hmac } from '@noble/hashes/hmac';
20
+ import { createRequire } from 'module';
21
+ import { readFileSync, existsSync } from 'fs';
22
+ import { dirname as dirname$1, join as join$1 } from 'path';
23
+ import { pathToFileURL as pathToFileURL$1, fileURLToPath as fileURLToPath$1 } from 'url';
20
24
 
21
25
  var __defProp = Object.defineProperty;
22
26
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -774,10 +778,10 @@ function globToRegexSource(pattern, opts = {}) {
774
778
  continue;
775
779
  }
776
780
  if (c === "[") {
777
- const compiled2 = compileBracket(pattern, i);
778
- if (compiled2) {
779
- out += guardDot() + compiled2.source;
780
- i = compiled2.next;
781
+ const compiled3 = compileBracket(pattern, i);
782
+ if (compiled3) {
783
+ out += guardDot() + compiled3.source;
784
+ i = compiled3.next;
781
785
  atSegmentStart = false;
782
786
  continue;
783
787
  }
@@ -5690,8 +5694,101 @@ function strerrorOf(e) {
5690
5694
  var EXIT_NOT_EXECUTABLE = 126;
5691
5695
  var EXIT_NOT_FOUND = 127;
5692
5696
 
5697
+ // src/wasi/constants.ts
5698
+ var WASI_ESUCCESS = 0;
5699
+ var WASI_EACCES = 2;
5700
+ var WASI_EAGAIN = 6;
5701
+ var WASI_EBADF = 8;
5702
+ var WASI_EBUSY = 10;
5703
+ var WASI_EEXIST = 20;
5704
+ var WASI_EFAULT = 21;
5705
+ var WASI_EFBIG = 22;
5706
+ var WASI_EINVAL = 28;
5707
+ var WASI_EIO = 29;
5708
+ var WASI_EISDIR = 31;
5709
+ var WASI_ELOOP = 32;
5710
+ var WASI_EMFILE = 34;
5711
+ var WASI_ENAMETOOLONG = 37;
5712
+ var WASI_ENOENT = 44;
5713
+ var WASI_ENOMEM = 48;
5714
+ var WASI_ENOSPC = 51;
5715
+ var WASI_ENOSYS = 52;
5716
+ var WASI_ENOTDIR = 54;
5717
+ var WASI_ENOTEMPTY = 55;
5718
+ var WASI_ENOTSUP = 58;
5719
+ var WASI_ENOTTY = 59;
5720
+ var WASI_EPERM = 63;
5721
+ var WASI_EPIPE = 64;
5722
+ var WASI_EROFS = 69;
5723
+ var WASI_ESPIPE = 70;
5724
+ var WASI_EXDEV = 75;
5725
+ var WASI_ENOTCAPABLE = 76;
5726
+ var WASI_ERRNO_BY_CODE = {
5727
+ EPERM: WASI_EPERM,
5728
+ ENOENT: WASI_ENOENT,
5729
+ EIO: WASI_EIO,
5730
+ EBADF: WASI_EBADF,
5731
+ EAGAIN: WASI_EAGAIN,
5732
+ ENOMEM: WASI_ENOMEM,
5733
+ EACCES: WASI_EACCES,
5734
+ EFAULT: WASI_EFAULT,
5735
+ EBUSY: WASI_EBUSY,
5736
+ EEXIST: WASI_EEXIST,
5737
+ EXDEV: WASI_EXDEV,
5738
+ ENOTDIR: WASI_ENOTDIR,
5739
+ EISDIR: WASI_EISDIR,
5740
+ EINVAL: WASI_EINVAL,
5741
+ EMFILE: WASI_EMFILE,
5742
+ ENOTTY: WASI_ENOTTY,
5743
+ EFBIG: WASI_EFBIG,
5744
+ ENOSPC: WASI_ENOSPC,
5745
+ ESPIPE: WASI_ESPIPE,
5746
+ EROFS: WASI_EROFS,
5747
+ EPIPE: WASI_EPIPE,
5748
+ ENAMETOOLONG: WASI_ENAMETOOLONG,
5749
+ ENOSYS: WASI_ENOSYS,
5750
+ ENOTEMPTY: WASI_ENOTEMPTY,
5751
+ ELOOP: WASI_ELOOP,
5752
+ ENOTSUP: WASI_ENOTSUP
5753
+ };
5754
+ var FILETYPE_UNKNOWN = 0;
5755
+ var FILETYPE_CHARACTER_DEVICE = 2;
5756
+ var FILETYPE_DIRECTORY = 3;
5757
+ var FILETYPE_REGULAR_FILE = 4;
5758
+ var FILETYPE_SYMBOLIC_LINK = 7;
5759
+ var O_CREAT = 1 << 0;
5760
+ var O_DIRECTORY = 1 << 1;
5761
+ var O_EXCL = 1 << 2;
5762
+ var O_TRUNC = 1 << 3;
5763
+ var FD_APPEND = 1 << 0;
5764
+ var LOOKUP_SYMLINK_FOLLOW = 1 << 0;
5765
+ var FST_ATIM = 1 << 0;
5766
+ var FST_ATIM_NOW = 1 << 1;
5767
+ var FST_MTIM = 1 << 2;
5768
+ var FST_MTIM_NOW = 1 << 3;
5769
+ var WHENCE_SET = 0;
5770
+ var WHENCE_CUR = 1;
5771
+ var WHENCE_END = 2;
5772
+ var EVENTTYPE_CLOCK = 0;
5773
+ var EVENTTYPE_FD_READ = 1;
5774
+ var EVENTTYPE_FD_WRITE = 2;
5775
+ var SUBSCRIPTION_CLOCK_ABSTIME = 1 << 0;
5776
+ var RIGHT_FD_READ = 1n << 1n;
5777
+ var RIGHT_FD_SEEK = 1n << 2n;
5778
+ var RIGHT_FD_WRITE = 1n << 6n;
5779
+ var RIGHTS_ALL = (1n << 30n) - 1n;
5780
+ var SIZEOF_FILESTAT = 64;
5781
+ var SIZEOF_FDSTAT = 24;
5782
+ var SIZEOF_DIRENT = 24;
5783
+ var SIZEOF_SUBSCRIPTION = 48;
5784
+ var SIZEOF_EVENT = 32;
5785
+ function isWasmBinary(bytes2) {
5786
+ return bytes2.length >= 4 && bytes2[0] === 0 && bytes2[1] === 97 && bytes2[2] === 115 && bytes2[3] === 109;
5787
+ }
5788
+
5693
5789
  // src/kernel/kernel.ts
5694
5790
  var BUILTIN_INTERPRETER = "/proc/self/exe";
5791
+ var WASI_INTERPRETER = "/usr/bin/wasi";
5695
5792
  var Kernel = class {
5696
5793
  vfs;
5697
5794
  procs = new ProcessTable();
@@ -5826,12 +5923,17 @@ var Kernel = class {
5826
5923
  if (command) return { kind: "builtin", path: candidate, command };
5827
5924
  }
5828
5925
  let head2 = "";
5926
+ let magic;
5829
5927
  try {
5830
5928
  if (this.vfs.permitted(st, R_OK, cred)) {
5831
- head2 = new TextDecoder().decode(this.vfs.readFile(real, cred).subarray(0, 256));
5929
+ magic = this.vfs.readFile(real, cred).subarray(0, 256);
5930
+ head2 = new TextDecoder().decode(magic);
5832
5931
  }
5833
5932
  } catch {
5834
5933
  }
5934
+ if (magic && isWasmBinary(magic)) {
5935
+ return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
5936
+ }
5835
5937
  if (head2.startsWith("#!")) {
5836
5938
  const lineEnd = head2.indexOf("\n");
5837
5939
  const line = (lineEnd >= 0 ? head2.slice(2, lineEnd) : head2.slice(2)).trim();
@@ -5845,6 +5947,7 @@ var Kernel = class {
5845
5947
  const ext = extname(real);
5846
5948
  if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return { kind: "script", path: real, interpreter: ["/usr/bin/node"] };
5847
5949
  if (ext === ".py") return { kind: "script", path: real, interpreter: ["/usr/bin/python3"] };
5950
+ if (ext === ".wasm") return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
5848
5951
  return { kind: "script", path: real, interpreter: ["/bin/sh"] };
5849
5952
  }
5850
5953
  return null;
@@ -15723,9 +15826,9 @@ function makeSum(name, algorithm, path) {
15723
15826
  if (args.has("check")) return checkSums(ctx, algorithm, args.positional, args.has("quiet") || args.has("status"));
15724
15827
  const { sources, ok: ok2 } = await readInputs(ctx, args.positional, { stdinName: "-" });
15725
15828
  for (const source of sources) {
15726
- const digest = await digestHex(algorithm, source.bytes);
15727
- if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${digest}`);
15728
- else ctx.line(`${digest} ${source.name}`);
15829
+ const digest2 = await digestHex(algorithm, source.bytes);
15830
+ if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${digest2}`);
15831
+ else ctx.line(`${digest2} ${source.name}`);
15729
15832
  }
15730
15833
  return ok2 ? 0 : 1;
15731
15834
  }
@@ -18506,12 +18609,12 @@ function installSyscallBridge(py) {
18506
18609
  function bindProgram(py, binding) {
18507
18610
  py.__sbxSlot.current = binding;
18508
18611
  const { ctx, stdin } = binding;
18509
- const decoder8 = new TextDecoder();
18612
+ const decoder9 = new TextDecoder();
18510
18613
  py.setStdout({
18511
- write: (buffer) => (ctx.write(decoder8.decode(buffer)), buffer.length)
18614
+ write: (buffer) => (ctx.write(decoder9.decode(buffer)), buffer.length)
18512
18615
  });
18513
18616
  py.setStderr({
18514
- write: (buffer) => (ctx.stderr.write(decoder8.decode(buffer)), buffer.length)
18617
+ write: (buffer) => (ctx.stderr.write(decoder9.decode(buffer)), buffer.length)
18515
18618
  });
18516
18619
  py.setStdin({
18517
18620
  read: (buffer) => {
@@ -18561,9 +18664,9 @@ async function resolveIndexUrl() {
18561
18664
  if (indexUrl) return indexUrl;
18562
18665
  if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
18563
18666
  try {
18564
- const { createRequire } = await nodeBuiltin("module");
18667
+ const { createRequire: createRequire2 } = await nodeBuiltin("module");
18565
18668
  const path = await nodeBuiltin("path");
18566
- const require2 = createRequire(path.join(process.cwd(), "index.js"));
18669
+ const require2 = createRequire2(path.join(process.cwd(), "index.js"));
18567
18670
  return `${path.dirname(require2.resolve("pyodide/package.json"))}/`;
18568
18671
  } catch {
18569
18672
  return void 0;
@@ -19028,15 +19131,15 @@ function pythonCommands() {
19028
19131
  var INSTALL_HINT = "ffmpeg is not installed in this container.\nThe FFmpeg runtime ships separately because it is a ~31MB WebAssembly build:\n npm install @ffmpeg/core";
19029
19132
  var compiled = null;
19030
19133
  async function loadFactory() {
19031
- const { createRequire } = await nodeBuiltin("module");
19134
+ const { createRequire: createRequire2 } = await nodeBuiltin("module");
19032
19135
  const fs = await nodeBuiltin("fs/promises");
19033
19136
  const path = await nodeBuiltin("path");
19034
- const packageRequire = createRequire(import.meta.url);
19137
+ const packageRequire = createRequire2(import.meta.url);
19035
19138
  let require2 = packageRequire;
19036
19139
  try {
19037
19140
  require2.resolve("@ffmpeg/core/wasm");
19038
19141
  } catch {
19039
- require2 = createRequire(path.join(process.cwd(), "index.js"));
19142
+ require2 = createRequire2(path.join(process.cwd(), "index.js"));
19040
19143
  }
19041
19144
  const wasmPath = require2.resolve("@ffmpeg/core/wasm");
19042
19145
  const wasm = await fs.readFile(wasmPath);
@@ -19125,6 +19228,1001 @@ var ffprobe = defineCommand({
19125
19228
  function ffmpegCommands() {
19126
19229
  return [ffmpeg, ffprobe];
19127
19230
  }
19231
+
19232
+ // src/wasi/host.ts
19233
+ init_path();
19234
+ init_errno();
19235
+ init_mode();
19236
+ var WasiExit = class extends Error {
19237
+ constructor(code) {
19238
+ super(`wasi exit ${code}`);
19239
+ this.code = code;
19240
+ this.name = "WasiExit";
19241
+ }
19242
+ code;
19243
+ };
19244
+ var encoder6 = new TextEncoder();
19245
+ var decoder7 = new TextDecoder();
19246
+ var FIRST_PREOPEN = 3;
19247
+ var WasiHost = class {
19248
+ memory;
19249
+ fds = /* @__PURE__ */ new Map();
19250
+ nextFd = FIRST_PREOPEN;
19251
+ opts;
19252
+ startTime;
19253
+ /** Set once `proc_exit` has run, so the runner reports the guest's code. */
19254
+ exitCode = null;
19255
+ constructor(options) {
19256
+ this.opts = {
19257
+ now: Date.now,
19258
+ hrtime: defaultHrtime,
19259
+ random: defaultRandom,
19260
+ sleep: defaultSleep,
19261
+ aborted: () => false,
19262
+ ...options
19263
+ };
19264
+ this.startTime = this.opts.hrtime();
19265
+ this.fds.set(0, { kind: "stdin" });
19266
+ this.fds.set(1, { kind: "stdout", stream: options.stdout });
19267
+ this.fds.set(2, { kind: "stderr", stream: options.stderr });
19268
+ const preopens = options.preopens ?? { "/": "/" };
19269
+ for (const [name, path] of Object.entries(preopens)) {
19270
+ const abs = resolve("/", path);
19271
+ this.fds.set(this.nextFd++, {
19272
+ kind: "dir",
19273
+ path: abs,
19274
+ root: abs,
19275
+ preopen: name,
19276
+ rights: RIGHTS_ALL
19277
+ });
19278
+ }
19279
+ }
19280
+ /** Attach the instance's memory. Called before `_start`. */
19281
+ bind(instance) {
19282
+ const exported = instance.exports.memory;
19283
+ if (!(exported instanceof WebAssembly.Memory)) {
19284
+ throw new Error("wasi: the module does not export a linear memory");
19285
+ }
19286
+ this.memory = exported;
19287
+ }
19288
+ /** Flush every buffered write. Called by the runner when the guest ends. */
19289
+ flushAll() {
19290
+ for (const fd of this.fds.values()) if (fd.kind === "file") this.writeBack(fd);
19291
+ }
19292
+ /**
19293
+ * `wasi_snapshot_preview1`.
19294
+ *
19295
+ * Every entry returns an errno rather than throwing: a JavaScript exception
19296
+ * crossing back into WebAssembly traps the instance, which turns a missing
19297
+ * file into an unrecoverable crash instead of the `ENOENT` the guest is
19298
+ * written to handle. `guard` is what enforces that.
19299
+ */
19300
+ get wasiImport() {
19301
+ const g = (fn) => ((...args) => this.guard(() => fn.apply(this, args)));
19302
+ return {
19303
+ args_get: g(this.args_get),
19304
+ args_sizes_get: g(this.args_sizes_get),
19305
+ environ_get: g(this.environ_get),
19306
+ environ_sizes_get: g(this.environ_sizes_get),
19307
+ clock_res_get: g(this.clock_res_get),
19308
+ clock_time_get: g(this.clock_time_get),
19309
+ fd_advise: g(() => WASI_ESUCCESS),
19310
+ fd_allocate: g(this.fd_allocate),
19311
+ fd_close: g(this.fd_close),
19312
+ fd_datasync: g(this.fd_sync),
19313
+ fd_fdstat_get: g(this.fd_fdstat_get),
19314
+ fd_fdstat_set_flags: g(this.fd_fdstat_set_flags),
19315
+ fd_fdstat_set_rights: g(() => WASI_ESUCCESS),
19316
+ fd_filestat_get: g(this.fd_filestat_get),
19317
+ fd_filestat_set_size: g(this.fd_filestat_set_size),
19318
+ fd_filestat_set_times: g(this.fd_filestat_set_times),
19319
+ fd_pread: g(this.fd_pread),
19320
+ fd_prestat_get: g(this.fd_prestat_get),
19321
+ fd_prestat_dir_name: g(this.fd_prestat_dir_name),
19322
+ fd_pwrite: g(this.fd_pwrite),
19323
+ fd_read: g(this.fd_read),
19324
+ fd_readdir: g(this.fd_readdir),
19325
+ fd_renumber: g(this.fd_renumber),
19326
+ fd_seek: g(this.fd_seek),
19327
+ fd_sync: g(this.fd_sync),
19328
+ fd_tell: g(this.fd_tell),
19329
+ fd_write: g(this.fd_write),
19330
+ path_create_directory: g(this.path_create_directory),
19331
+ path_filestat_get: g(this.path_filestat_get),
19332
+ path_filestat_set_times: g(this.path_filestat_set_times),
19333
+ path_link: g(this.path_link),
19334
+ path_open: g(this.path_open),
19335
+ path_readlink: g(this.path_readlink),
19336
+ path_remove_directory: g(this.path_remove_directory),
19337
+ path_rename: g(this.path_rename),
19338
+ path_symlink: g(this.path_symlink),
19339
+ path_unlink_file: g(this.path_unlink_file),
19340
+ poll_oneoff: g(this.poll_oneoff),
19341
+ proc_exit: ((code) => {
19342
+ this.exitCode = code;
19343
+ throw new WasiExit(code);
19344
+ }),
19345
+ proc_raise: g(() => WASI_ENOSYS),
19346
+ random_get: g(this.random_get),
19347
+ sched_yield: g(() => WASI_ESUCCESS),
19348
+ /* Sockets are the one part of WASI this container cannot honour: its
19349
+ * network is an HTTP router, not a packet path. Saying so with ENOTSUP
19350
+ * lets a guest fall back, where a trap would just kill it. */
19351
+ sock_accept: g(() => WASI_ENOTSUP),
19352
+ sock_recv: g(() => WASI_ENOTSUP),
19353
+ sock_send: g(() => WASI_ENOTSUP),
19354
+ sock_shutdown: g(() => WASI_ENOTSUP)
19355
+ };
19356
+ }
19357
+ /**
19358
+ * `wasi_unstable`, the preview0 name older toolchains still emit.
19359
+ *
19360
+ * Identical but for `fd_seek`, whose `whence` values were reordered before
19361
+ * preview1 was frozen. Aliasing the table without this correction is a
19362
+ * popular bug: every seek in an old binary lands somewhere plausible and
19363
+ * wrong.
19364
+ */
19365
+ get wasiUnstableImport() {
19366
+ const preview0Whence = [WHENCE_CUR, WHENCE_END, WHENCE_SET];
19367
+ return {
19368
+ ...this.wasiImport,
19369
+ fd_seek: ((fd, offset, whence, out) => this.guard(
19370
+ () => this.fd_seek(fd, offset, preview0Whence[whence] ?? WHENCE_SET, out)
19371
+ ))
19372
+ };
19373
+ }
19374
+ // ── memory access ────────────────────────────────────────────────────────
19375
+ //
19376
+ // Views are built per call rather than cached: `memory.grow` detaches every
19377
+ // existing view, so a cached one turns into a silent zero-length buffer the
19378
+ // first time the guest's allocator asks for another page.
19379
+ get view() {
19380
+ if (!this.memory) throw new Error("wasi: memory is not bound");
19381
+ return new DataView(this.memory.buffer);
19382
+ }
19383
+ get bytes() {
19384
+ if (!this.memory) throw new Error("wasi: memory is not bound");
19385
+ return new Uint8Array(this.memory.buffer);
19386
+ }
19387
+ readString(ptr, len) {
19388
+ return decoder7.decode(this.bytes.subarray(ptr, ptr + len));
19389
+ }
19390
+ /** The scatter/gather list `fd_read` and `fd_write` are given. */
19391
+ iovecs(ptr, count) {
19392
+ const view = this.view;
19393
+ const out = [];
19394
+ for (let i = 0; i < count; i++) {
19395
+ const base2 = ptr + i * 8;
19396
+ out.push({ offset: view.getUint32(base2, true), length: view.getUint32(base2 + 4, true) });
19397
+ }
19398
+ return out;
19399
+ }
19400
+ /**
19401
+ * Turn anything thrown inside a syscall into an errno.
19402
+ *
19403
+ * `WasiExit` is re-thrown on purpose: it is the guest unwinding its own
19404
+ * stack, not a failure to be reported through a return value.
19405
+ */
19406
+ guard(fn) {
19407
+ try {
19408
+ return fn();
19409
+ } catch (error) {
19410
+ if (error instanceof WasiExit) throw error;
19411
+ return errnoOf(error);
19412
+ }
19413
+ }
19414
+ // ── descriptors ──────────────────────────────────────────────────────────
19415
+ get(fd) {
19416
+ const found = this.fds.get(fd);
19417
+ if (!found) throw new WasiError(WASI_EBADF);
19418
+ return found;
19419
+ }
19420
+ dir(fd) {
19421
+ const found = this.get(fd);
19422
+ if (found.kind !== "dir") throw new WasiError(WASI_ENOTDIR);
19423
+ return found;
19424
+ }
19425
+ file(fd) {
19426
+ const found = this.get(fd);
19427
+ if (found.kind !== "file") throw new WasiError(WASI_EBADF);
19428
+ return found;
19429
+ }
19430
+ /**
19431
+ * Resolve a guest path against a directory descriptor.
19432
+ *
19433
+ * The confinement check is the one place preopens are enforced: a path that
19434
+ * climbs out of the directory the descriptor was derived from is
19435
+ * `ENOTCAPABLE`, which is exactly the error a capability-oriented guest
19436
+ * expects and knows how to report.
19437
+ */
19438
+ resolveAt(dirfd, path) {
19439
+ const dir3 = this.dir(dirfd);
19440
+ if (path === "") throw new WasiError(WASI_ENOENT);
19441
+ const abs = isAbsolute(path) ? resolve("/", path) : resolve(dir3.path, path);
19442
+ const root = dir3.root;
19443
+ if (root !== "/" && abs !== root && !abs.startsWith(root.endsWith("/") ? root : `${root}/`)) {
19444
+ throw new WasiError(WASI_ENOTCAPABLE);
19445
+ }
19446
+ return { abs, root };
19447
+ }
19448
+ allocate(descriptor) {
19449
+ const fd = this.nextFd++;
19450
+ this.fds.set(fd, descriptor);
19451
+ return fd;
19452
+ }
19453
+ /**
19454
+ * Push a buffered file back to the VFS.
19455
+ *
19456
+ * Called before every path-based call as well as on close, so a guest that
19457
+ * writes a file and then stats or reopens it by name sees what it wrote —
19458
+ * the alternative is a stale read that looks like data loss.
19459
+ */
19460
+ writeBack(fd) {
19461
+ if (!fd.dirty) return;
19462
+ this.opts.vfs.writeFile(fd.path, fd.data.subarray(0, fd.size), { cred: this.opts.cred });
19463
+ fd.dirty = false;
19464
+ }
19465
+ syncPaths() {
19466
+ for (const fd of this.fds.values()) if (fd.kind === "file" && fd.dirty) this.writeBack(fd);
19467
+ }
19468
+ // ── args and environment ─────────────────────────────────────────────────
19469
+ args_get(argvPtr, bufPtr) {
19470
+ return this.writeStringVector(this.opts.argv, argvPtr, bufPtr);
19471
+ }
19472
+ args_sizes_get(countPtr, sizePtr) {
19473
+ return this.writeVectorSizes(this.opts.argv, countPtr, sizePtr);
19474
+ }
19475
+ get envStrings() {
19476
+ return Object.entries(this.opts.env).map(([key, value]) => `${key}=${value}`);
19477
+ }
19478
+ environ_get(envPtr, bufPtr) {
19479
+ return this.writeStringVector(this.envStrings, envPtr, bufPtr);
19480
+ }
19481
+ environ_sizes_get(countPtr, sizePtr) {
19482
+ return this.writeVectorSizes(this.envStrings, countPtr, sizePtr);
19483
+ }
19484
+ writeStringVector(values, pointersPtr, bufPtr) {
19485
+ const view = this.view;
19486
+ const memory = this.bytes;
19487
+ let cursor = bufPtr;
19488
+ values.forEach((value, index) => {
19489
+ view.setUint32(pointersPtr + index * 4, cursor, true);
19490
+ const encoded = encoder6.encode(`${value}\0`);
19491
+ memory.set(encoded, cursor);
19492
+ cursor += encoded.length;
19493
+ });
19494
+ return WASI_ESUCCESS;
19495
+ }
19496
+ writeVectorSizes(values, countPtr, sizePtr) {
19497
+ const view = this.view;
19498
+ view.setUint32(countPtr, values.length, true);
19499
+ view.setUint32(
19500
+ sizePtr,
19501
+ values.reduce((total, value) => total + encoder6.encode(value).length + 1, 0),
19502
+ true
19503
+ );
19504
+ return WASI_ESUCCESS;
19505
+ }
19506
+ // ── clocks and randomness ────────────────────────────────────────────────
19507
+ clock_res_get(_id, out) {
19508
+ this.view.setBigUint64(out, 1000000n, true);
19509
+ return WASI_ESUCCESS;
19510
+ }
19511
+ clock_time_get(id, _precision, out) {
19512
+ this.view.setBigUint64(out, this.clockNow(id), true);
19513
+ return WASI_ESUCCESS;
19514
+ }
19515
+ clockNow(id) {
19516
+ if (id === 0) return BigInt(Math.round(this.opts.now())) * 1000000n;
19517
+ return this.opts.hrtime() - this.startTime;
19518
+ }
19519
+ random_get(ptr, len) {
19520
+ const into = new Uint8Array(len);
19521
+ this.opts.random(into);
19522
+ this.bytes.set(into, ptr);
19523
+ return WASI_ESUCCESS;
19524
+ }
19525
+ // ── reading and writing ──────────────────────────────────────────────────
19526
+ fd_read(fd, iovsPtr, iovsLen, out) {
19527
+ const descriptor = this.get(fd);
19528
+ const iovs = this.iovecs(iovsPtr, iovsLen);
19529
+ let total = 0;
19530
+ if (descriptor.kind === "stdin") {
19531
+ for (const iov of iovs) {
19532
+ if (iov.length === 0) continue;
19533
+ const chunk = this.opts.stdin.read(iov.length);
19534
+ if (chunk.length === 0) break;
19535
+ this.bytes.set(chunk, iov.offset);
19536
+ total += chunk.length;
19537
+ if (chunk.length < iov.length) break;
19538
+ }
19539
+ this.view.setUint32(out, total, true);
19540
+ return WASI_ESUCCESS;
19541
+ }
19542
+ if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
19543
+ if ((descriptor.rights & RIGHT_FD_READ) === 0n) throw new WasiError(WASI_EACCES);
19544
+ for (const iov of iovs) {
19545
+ const end = Math.min(descriptor.pos + iov.length, descriptor.size);
19546
+ const chunk = descriptor.data.subarray(descriptor.pos, Math.max(end, descriptor.pos));
19547
+ if (chunk.length === 0) break;
19548
+ this.bytes.set(chunk, iov.offset);
19549
+ descriptor.pos += chunk.length;
19550
+ total += chunk.length;
19551
+ if (chunk.length < iov.length) break;
19552
+ }
19553
+ this.view.setUint32(out, total, true);
19554
+ return WASI_ESUCCESS;
19555
+ }
19556
+ fd_pread(fd, iovsPtr, iovsLen, offset, out) {
19557
+ const descriptor = this.get(fd);
19558
+ if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
19559
+ const saved = descriptor.pos;
19560
+ descriptor.pos = Number(offset);
19561
+ try {
19562
+ return this.fd_read(fd, iovsPtr, iovsLen, out);
19563
+ } finally {
19564
+ descriptor.pos = saved;
19565
+ }
19566
+ }
19567
+ fd_write(fd, iovsPtr, iovsLen, out) {
19568
+ const descriptor = this.get(fd);
19569
+ const iovs = this.iovecs(iovsPtr, iovsLen);
19570
+ let total = 0;
19571
+ if (descriptor.kind === "stdout" || descriptor.kind === "stderr") {
19572
+ for (const iov of iovs) {
19573
+ const chunk = this.bytes.slice(iov.offset, iov.offset + iov.length);
19574
+ try {
19575
+ descriptor.stream.write(chunk);
19576
+ } catch (error) {
19577
+ if (isSysError(error) && error.code === "EPIPE") throw new WasiError(WASI_EPIPE);
19578
+ throw error;
19579
+ }
19580
+ total += chunk.length;
19581
+ }
19582
+ this.view.setUint32(out, total, true);
19583
+ return WASI_ESUCCESS;
19584
+ }
19585
+ if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
19586
+ if ((descriptor.rights & RIGHT_FD_WRITE) === 0n) throw new WasiError(WASI_EACCES);
19587
+ if (descriptor.append) descriptor.pos = descriptor.size;
19588
+ for (const iov of iovs) {
19589
+ const chunk = this.bytes.subarray(iov.offset, iov.offset + iov.length);
19590
+ reserve(descriptor, descriptor.pos + chunk.length);
19591
+ descriptor.data.set(chunk, descriptor.pos);
19592
+ descriptor.pos += chunk.length;
19593
+ descriptor.size = Math.max(descriptor.size, descriptor.pos);
19594
+ total += chunk.length;
19595
+ }
19596
+ descriptor.dirty = true;
19597
+ this.view.setUint32(out, total, true);
19598
+ return WASI_ESUCCESS;
19599
+ }
19600
+ fd_pwrite(fd, iovsPtr, iovsLen, offset, out) {
19601
+ const descriptor = this.get(fd);
19602
+ if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
19603
+ const saved = descriptor.pos;
19604
+ const appended = descriptor.append;
19605
+ descriptor.pos = Number(offset);
19606
+ descriptor.append = false;
19607
+ try {
19608
+ return this.fd_write(fd, iovsPtr, iovsLen, out);
19609
+ } finally {
19610
+ descriptor.pos = saved;
19611
+ descriptor.append = appended;
19612
+ }
19613
+ }
19614
+ fd_seek(fd, offset, whence, out) {
19615
+ const descriptor = this.get(fd);
19616
+ if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
19617
+ if ((descriptor.rights & RIGHT_FD_SEEK) === 0n) throw new WasiError(WASI_EACCES);
19618
+ const base2 = whence === WHENCE_SET ? 0 : whence === WHENCE_CUR ? descriptor.pos : descriptor.size;
19619
+ const next = base2 + Number(offset);
19620
+ if (next < 0) throw new WasiError(WASI_EINVAL);
19621
+ descriptor.pos = next;
19622
+ this.view.setBigUint64(out, BigInt(next), true);
19623
+ return WASI_ESUCCESS;
19624
+ }
19625
+ fd_tell(fd, out) {
19626
+ this.view.setBigUint64(out, BigInt(this.file(fd).pos), true);
19627
+ return WASI_ESUCCESS;
19628
+ }
19629
+ fd_close(fd) {
19630
+ const descriptor = this.get(fd);
19631
+ if (descriptor.kind === "file") this.writeBack(descriptor);
19632
+ this.fds.delete(fd);
19633
+ return WASI_ESUCCESS;
19634
+ }
19635
+ fd_sync(fd) {
19636
+ const descriptor = this.get(fd);
19637
+ if (descriptor.kind === "file") this.writeBack(descriptor);
19638
+ return WASI_ESUCCESS;
19639
+ }
19640
+ fd_renumber(from, to) {
19641
+ const descriptor = this.get(from);
19642
+ const replaced = this.fds.get(to);
19643
+ if (replaced?.kind === "file") this.writeBack(replaced);
19644
+ this.fds.set(to, descriptor);
19645
+ this.fds.delete(from);
19646
+ return WASI_ESUCCESS;
19647
+ }
19648
+ fd_allocate(fd, offset, len) {
19649
+ const descriptor = this.file(fd);
19650
+ const size = Number(offset + len);
19651
+ if (descriptor.size < size) {
19652
+ reserve(descriptor, size);
19653
+ descriptor.data.fill(0, descriptor.size, size);
19654
+ descriptor.size = size;
19655
+ descriptor.dirty = true;
19656
+ }
19657
+ return WASI_ESUCCESS;
19658
+ }
19659
+ fd_filestat_set_size(fd, size) {
19660
+ const descriptor = this.file(fd);
19661
+ const length = Number(size);
19662
+ reserve(descriptor, length);
19663
+ if (length > descriptor.size) descriptor.data.fill(0, descriptor.size, length);
19664
+ descriptor.size = length;
19665
+ if (descriptor.pos > length) descriptor.pos = length;
19666
+ descriptor.dirty = true;
19667
+ return WASI_ESUCCESS;
19668
+ }
19669
+ // ── metadata ─────────────────────────────────────────────────────────────
19670
+ fd_fdstat_get(fd, out) {
19671
+ const descriptor = this.get(fd);
19672
+ const view = this.view;
19673
+ const [filetype, flags, rights] = (() => {
19674
+ switch (descriptor.kind) {
19675
+ case "dir":
19676
+ return [FILETYPE_DIRECTORY, 0, descriptor.rights];
19677
+ case "file":
19678
+ return [FILETYPE_REGULAR_FILE, descriptor.flags, descriptor.rights];
19679
+ default:
19680
+ return [FILETYPE_CHARACTER_DEVICE, 0, RIGHTS_ALL];
19681
+ }
19682
+ })();
19683
+ zero(this.bytes, out, SIZEOF_FDSTAT);
19684
+ view.setUint8(out, filetype);
19685
+ view.setUint16(out + 2, flags, true);
19686
+ view.setBigUint64(out + 8, rights, true);
19687
+ view.setBigUint64(out + 16, rights, true);
19688
+ return WASI_ESUCCESS;
19689
+ }
19690
+ fd_fdstat_set_flags(fd, flags) {
19691
+ const descriptor = this.file(fd);
19692
+ descriptor.flags = flags;
19693
+ descriptor.append = (flags & FD_APPEND) !== 0;
19694
+ return WASI_ESUCCESS;
19695
+ }
19696
+ fd_filestat_get(fd, out) {
19697
+ const descriptor = this.get(fd);
19698
+ if (descriptor.kind === "file" || descriptor.kind === "dir") {
19699
+ this.syncPaths();
19700
+ this.writeFilestat(out, this.opts.vfs.stat(descriptor.path, { cred: this.opts.cred }));
19701
+ return WASI_ESUCCESS;
19702
+ }
19703
+ zero(this.bytes, out, SIZEOF_FILESTAT);
19704
+ this.view.setUint8(out + 16, FILETYPE_CHARACTER_DEVICE);
19705
+ return WASI_ESUCCESS;
19706
+ }
19707
+ fd_filestat_set_times(fd, atim, mtim, flags) {
19708
+ const descriptor = this.get(fd);
19709
+ if (descriptor.kind !== "file" && descriptor.kind !== "dir") return WASI_ESUCCESS;
19710
+ return this.setTimes(descriptor.path, atim, mtim, flags);
19711
+ }
19712
+ path_filestat_get(dirfd, lookupFlags, pathPtr, pathLen, out) {
19713
+ this.syncPaths();
19714
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19715
+ const follow = (lookupFlags & LOOKUP_SYMLINK_FOLLOW) !== 0;
19716
+ this.writeFilestat(
19717
+ out,
19718
+ follow ? this.opts.vfs.stat(abs, { cred: this.opts.cred }) : this.opts.vfs.lstat(abs)
19719
+ );
19720
+ return WASI_ESUCCESS;
19721
+ }
19722
+ path_filestat_set_times(dirfd, _lookupFlags, pathPtr, pathLen, atim, mtim, flags) {
19723
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19724
+ return this.setTimes(abs, atim, mtim, flags);
19725
+ }
19726
+ setTimes(path, atim, mtim, flags) {
19727
+ const current = this.opts.vfs.stat(path, { cred: this.opts.cred });
19728
+ const now = this.opts.now();
19729
+ const atime = flags & FST_ATIM_NOW ? now : flags & FST_ATIM ? Number(atim / 1000000n) : current.atimeMs;
19730
+ const mtime = flags & FST_MTIM_NOW ? now : flags & FST_MTIM ? Number(mtim / 1000000n) : current.mtimeMs;
19731
+ this.opts.vfs.utimes(path, atime, mtime, this.opts.cred);
19732
+ return WASI_ESUCCESS;
19733
+ }
19734
+ writeFilestat(out, stat2) {
19735
+ const view = this.view;
19736
+ zero(this.bytes, out, SIZEOF_FILESTAT);
19737
+ view.setBigUint64(out, BigInt(stat2.dev), true);
19738
+ view.setBigUint64(out + 8, BigInt(stat2.ino), true);
19739
+ view.setUint8(out + 16, filetypeOf(stat2.mode));
19740
+ view.setBigUint64(out + 24, BigInt(stat2.nlink), true);
19741
+ view.setBigUint64(out + 32, BigInt(stat2.size), true);
19742
+ view.setBigUint64(out + 40, msToNs(stat2.atimeMs), true);
19743
+ view.setBigUint64(out + 48, msToNs(stat2.mtimeMs), true);
19744
+ view.setBigUint64(out + 56, msToNs(stat2.ctimeMs), true);
19745
+ }
19746
+ // ── preopens ─────────────────────────────────────────────────────────────
19747
+ fd_prestat_get(fd, out) {
19748
+ const descriptor = this.fds.get(fd);
19749
+ if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
19750
+ throw new WasiError(WASI_EBADF);
19751
+ }
19752
+ const view = this.view;
19753
+ view.setUint8(
19754
+ out,
19755
+ 0
19756
+ /* preopentype: dir */
19757
+ );
19758
+ view.setUint32(out + 4, encoder6.encode(descriptor.preopen).length, true);
19759
+ return WASI_ESUCCESS;
19760
+ }
19761
+ fd_prestat_dir_name(fd, ptr, len) {
19762
+ const descriptor = this.fds.get(fd);
19763
+ if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
19764
+ throw new WasiError(WASI_EBADF);
19765
+ }
19766
+ const name = encoder6.encode(descriptor.preopen);
19767
+ if (name.length > len) throw new WasiError(WASI_EINVAL);
19768
+ this.bytes.set(name, ptr);
19769
+ return WASI_ESUCCESS;
19770
+ }
19771
+ // ── paths ────────────────────────────────────────────────────────────────
19772
+ path_open(dirfd, _lookupFlags, pathPtr, pathLen, oflags, rightsBase, _rightsInheriting, fdflags, out) {
19773
+ this.syncPaths();
19774
+ const { abs, root } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19775
+ const vfs = this.opts.vfs;
19776
+ const cred = this.opts.cred;
19777
+ const exists = vfs.lexists(abs);
19778
+ if (oflags & O_EXCL && oflags & O_CREAT && exists) throw new WasiError(WASI_EEXIST);
19779
+ if (exists && vfs.stat(abs, { cred }).isDirectory()) {
19780
+ if (rightsBase & RIGHT_FD_WRITE) throw new WasiError(WASI_EISDIR);
19781
+ this.view.setUint32(out, this.allocate({ kind: "dir", path: abs, root, rights: RIGHTS_ALL }), true);
19782
+ return WASI_ESUCCESS;
19783
+ }
19784
+ if (oflags & O_DIRECTORY) throw new WasiError(exists ? WASI_ENOTDIR : WASI_ENOENT);
19785
+ if (!exists) {
19786
+ if (!(oflags & O_CREAT)) throw new WasiError(WASI_ENOENT);
19787
+ vfs.writeFile(abs, new Uint8Array(0), { cred, mode: 420 });
19788
+ }
19789
+ const truncate = (oflags & O_TRUNC) !== 0;
19790
+ const data = truncate ? new Uint8Array(0) : vfs.readFile(abs, cred);
19791
+ if (truncate) vfs.writeFile(abs, data, { cred });
19792
+ const size = data.length;
19793
+ const rights = rightsBase === 0n ? RIGHTS_ALL : rightsBase;
19794
+ const append = (fdflags & FD_APPEND) !== 0;
19795
+ this.view.setUint32(
19796
+ out,
19797
+ this.allocate({
19798
+ kind: "file",
19799
+ path: abs,
19800
+ root,
19801
+ data,
19802
+ size,
19803
+ pos: append ? size : 0,
19804
+ dirty: false,
19805
+ append,
19806
+ rights,
19807
+ flags: fdflags
19808
+ }),
19809
+ true
19810
+ );
19811
+ return WASI_ESUCCESS;
19812
+ }
19813
+ fd_readdir(fd, buf, bufLen, cookie, out) {
19814
+ this.syncPaths();
19815
+ const descriptor = this.dir(fd);
19816
+ const vfs = this.opts.vfs;
19817
+ const cred = this.opts.cred;
19818
+ const entries = [
19819
+ { name: ".", type: FILETYPE_DIRECTORY },
19820
+ { name: "..", type: FILETYPE_DIRECTORY },
19821
+ ...vfs.readdirWithTypes(descriptor.path, cred).map((entry) => ({
19822
+ name: entry.name,
19823
+ type: entry.kind === "directory" ? FILETYPE_DIRECTORY : entry.kind === "symlink" ? FILETYPE_SYMBOLIC_LINK : entry.kind === "file" ? FILETYPE_REGULAR_FILE : FILETYPE_UNKNOWN
19824
+ }))
19825
+ ];
19826
+ const view = this.view;
19827
+ const memory = this.bytes;
19828
+ let written = 0;
19829
+ for (let index = Number(cookie); index < entries.length; index++) {
19830
+ const entry = entries[index];
19831
+ const name = encoder6.encode(entry.name);
19832
+ if (written + SIZEOF_DIRENT > bufLen) break;
19833
+ const at = buf + written;
19834
+ view.setBigUint64(at, BigInt(index + 1), true);
19835
+ view.setBigUint64(at + 8, BigInt(inodeOf(descriptor.path, entry.name)), true);
19836
+ view.setUint32(at + 16, name.length, true);
19837
+ view.setUint8(at + 20, entry.type);
19838
+ written += SIZEOF_DIRENT;
19839
+ const room = Math.min(name.length, bufLen - written);
19840
+ memory.set(name.subarray(0, room), buf + written);
19841
+ written += room;
19842
+ if (room < name.length) break;
19843
+ }
19844
+ view.setUint32(out, written, true);
19845
+ return WASI_ESUCCESS;
19846
+ }
19847
+ path_create_directory(dirfd, pathPtr, pathLen) {
19848
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19849
+ this.opts.vfs.mkdir(abs, { cred: this.opts.cred, mode: 493 });
19850
+ return WASI_ESUCCESS;
19851
+ }
19852
+ path_remove_directory(dirfd, pathPtr, pathLen) {
19853
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19854
+ this.opts.vfs.rmdir(abs, this.opts.cred);
19855
+ return WASI_ESUCCESS;
19856
+ }
19857
+ path_unlink_file(dirfd, pathPtr, pathLen) {
19858
+ this.syncPaths();
19859
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19860
+ this.opts.vfs.unlink(abs, this.opts.cred);
19861
+ return WASI_ESUCCESS;
19862
+ }
19863
+ path_rename(oldDirfd, oldPtr, oldLen, newDirfd, newPtr, newLen) {
19864
+ this.syncPaths();
19865
+ const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
19866
+ const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
19867
+ this.opts.vfs.rename(from.abs, to.abs, this.opts.cred);
19868
+ return WASI_ESUCCESS;
19869
+ }
19870
+ path_symlink(targetPtr, targetLen, dirfd, pathPtr, pathLen) {
19871
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19872
+ this.opts.vfs.symlink(this.readString(targetPtr, targetLen), abs, this.opts.cred);
19873
+ return WASI_ESUCCESS;
19874
+ }
19875
+ path_link(oldDirfd, _lookupFlags, oldPtr, oldLen, newDirfd, newPtr, newLen) {
19876
+ this.syncPaths();
19877
+ const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
19878
+ const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
19879
+ this.opts.vfs.link(from.abs, to.abs, this.opts.cred);
19880
+ return WASI_ESUCCESS;
19881
+ }
19882
+ path_readlink(dirfd, pathPtr, pathLen, buf, bufLen, out) {
19883
+ const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
19884
+ const target = encoder6.encode(this.opts.vfs.readlink(abs, this.opts.cred));
19885
+ const room = Math.min(target.length, bufLen);
19886
+ this.bytes.set(target.subarray(0, room), buf);
19887
+ this.view.setUint32(out, room, true);
19888
+ return WASI_ESUCCESS;
19889
+ }
19890
+ // ── polling ──────────────────────────────────────────────────────────────
19891
+ /**
19892
+ * `poll_oneoff`, which is how a WASI guest sleeps and how it waits on I/O.
19893
+ *
19894
+ * Files and the standard streams are always ready here — nothing in this
19895
+ * container can leave a read pending, since stdin was drained before the
19896
+ * guest started. That leaves the clock, which is the case that matters:
19897
+ * `sleep()` compiles to a lone clock subscription, and it is honoured by
19898
+ * actually waiting, in slices, so that killing the process interrupts it.
19899
+ */
19900
+ poll_oneoff(inPtr, outPtr, count, nevents) {
19901
+ if (count === 0) {
19902
+ this.view.setUint32(nevents, 0, true);
19903
+ return WASI_ESUCCESS;
19904
+ }
19905
+ const subscriptions = [];
19906
+ for (let index = 0; index < count; index++) {
19907
+ const at = inPtr + index * SIZEOF_SUBSCRIPTION;
19908
+ const view2 = this.view;
19909
+ subscriptions.push({
19910
+ userdata: view2.getBigUint64(at, true),
19911
+ type: view2.getUint8(at + 8),
19912
+ fd: view2.getUint32(at + 16, true),
19913
+ clockId: view2.getUint32(at + 16, true),
19914
+ timeout: view2.getBigUint64(at + 24, true),
19915
+ flags: view2.getUint16(at + 40, true)
19916
+ });
19917
+ }
19918
+ const ready = subscriptions.filter((s) => s.type !== EVENTTYPE_CLOCK);
19919
+ if (ready.length === 0) {
19920
+ const delays = subscriptions.map((s) => {
19921
+ const nanoseconds = (s.flags & SUBSCRIPTION_CLOCK_ABSTIME) !== 0 ? s.timeout - this.clockNow(s.clockId) : s.timeout;
19922
+ return Number(nanoseconds < 0n ? 0n : nanoseconds) / 1e6;
19923
+ });
19924
+ this.sleepInterruptibly(Math.min(...delays));
19925
+ }
19926
+ const events = ready.length > 0 ? ready : subscriptions;
19927
+ const view = this.view;
19928
+ events.forEach((subscription, index) => {
19929
+ const at = outPtr + index * SIZEOF_EVENT;
19930
+ zero(this.bytes, at, SIZEOF_EVENT);
19931
+ view.setBigUint64(at, subscription.userdata, true);
19932
+ view.setUint16(at + 8, WASI_ESUCCESS, true);
19933
+ view.setUint8(at + 10, subscription.type);
19934
+ if (subscription.type === EVENTTYPE_FD_READ) {
19935
+ const available = subscription.fd === 0 ? this.opts.stdin.available : this.bytesLeft(subscription.fd);
19936
+ view.setBigUint64(at + 16, BigInt(available), true);
19937
+ } else if (subscription.type === EVENTTYPE_FD_WRITE) {
19938
+ view.setBigUint64(at + 16, BigInt(64 * 1024), true);
19939
+ }
19940
+ });
19941
+ view.setUint32(nevents, events.length, true);
19942
+ return WASI_ESUCCESS;
19943
+ }
19944
+ bytesLeft(fd) {
19945
+ const descriptor = this.fds.get(fd);
19946
+ return descriptor?.kind === "file" ? Math.max(0, descriptor.size - descriptor.pos) : 0;
19947
+ }
19948
+ /** Wait in slices so that a `SIGKILL` does not have to outlast the sleep. */
19949
+ sleepInterruptibly(milliseconds) {
19950
+ let remaining = milliseconds;
19951
+ while (remaining > 0) {
19952
+ if (this.opts.aborted()) return;
19953
+ const slice = Math.min(remaining, 50);
19954
+ this.opts.sleep(slice);
19955
+ remaining -= slice;
19956
+ }
19957
+ }
19958
+ };
19959
+ var WasiError = class extends Error {
19960
+ constructor(errno) {
19961
+ super(`wasi errno ${errno}`);
19962
+ this.errno = errno;
19963
+ this.name = "WasiError";
19964
+ }
19965
+ errno;
19966
+ };
19967
+ function errnoOf(error) {
19968
+ if (error instanceof WasiError) return error.errno;
19969
+ if (isSysError(error)) return WASI_ERRNO_BY_CODE[error.code] ?? WASI_EIO;
19970
+ if (error instanceof RangeError) return WASI_EFAULT;
19971
+ return WASI_EIO;
19972
+ }
19973
+ function filetypeOf(mode) {
19974
+ const kind = mode & 61440;
19975
+ if (kind === S_IFDIR) return FILETYPE_DIRECTORY;
19976
+ if (kind === S_IFREG) return FILETYPE_REGULAR_FILE;
19977
+ if (kind === S_IFLNK) return FILETYPE_SYMBOLIC_LINK;
19978
+ if (kind === S_IFCHR) return FILETYPE_CHARACTER_DEVICE;
19979
+ return FILETYPE_UNKNOWN;
19980
+ }
19981
+ function msToNs(milliseconds) {
19982
+ return BigInt(Math.round(milliseconds)) * 1000000n;
19983
+ }
19984
+ function zero(memory, at, length) {
19985
+ memory.fill(0, at, at + length);
19986
+ }
19987
+ function reserve(descriptor, length) {
19988
+ if (length <= descriptor.data.length) return;
19989
+ const grown = new Uint8Array(Math.max(length, descriptor.data.length * 2, 1024));
19990
+ grown.set(descriptor.data.subarray(0, descriptor.size));
19991
+ descriptor.data = grown;
19992
+ }
19993
+ function inodeOf(dir3, name) {
19994
+ let hash = 2166136261;
19995
+ const path = `${dir3}/${name}`;
19996
+ for (let index = 0; index < path.length; index++) {
19997
+ hash ^= path.charCodeAt(index);
19998
+ hash = Math.imul(hash, 16777619);
19999
+ }
20000
+ return hash >>> 0;
20001
+ }
20002
+ function defaultHrtime() {
20003
+ return BigInt(Math.round(performance.now() * 1e6));
20004
+ }
20005
+ function defaultRandom(into) {
20006
+ crypto.getRandomValues(into);
20007
+ }
20008
+ function defaultSleep(milliseconds) {
20009
+ try {
20010
+ const buffer = new Int32Array(new SharedArrayBuffer(4));
20011
+ Atomics.wait(buffer, 0, 0, milliseconds);
20012
+ } catch {
20013
+ }
20014
+ }
20015
+
20016
+ // src/wasi/run.ts
20017
+ var compiled2 = /* @__PURE__ */ new Map();
20018
+ var MAX_CACHED_MODULES = 32;
20019
+ async function runWasi(ctx, bytes2, options = {}) {
20020
+ if (!isWasmBinary(bytes2)) {
20021
+ ctx.warn("cannot execute binary file: not a WebAssembly module");
20022
+ return EXIT_NOT_EXECUTABLE;
20023
+ }
20024
+ const argv = options.argv ?? ctx.argv;
20025
+ const host2 = new WasiHost({
20026
+ argv,
20027
+ /* `PWD` is set truthfully, but do not mistake it for a working directory:
20028
+ * wasi-libc keeps its own cwd, fixed at `/`, and ignores this. A guest's
20029
+ * relative path therefore resolves from the root — the same thing it does
20030
+ * under wasmtime, and why programs run here are given absolute paths. */
20031
+ env: { ...ctx.env, PWD: ctx.cwd },
20032
+ vfs: ctx.vfs,
20033
+ cred: ctx.cred,
20034
+ cwd: ctx.cwd,
20035
+ stdin: await primeStdin(ctx),
20036
+ stdout: ctx.stdout,
20037
+ stderr: ctx.stderr,
20038
+ ...options.preopens ? { preopens: options.preopens } : {},
20039
+ now: ctx.kernel.now,
20040
+ aborted: () => ctx.signal.aborted
20041
+ });
20042
+ let instance;
20043
+ try {
20044
+ const module = await compile(bytes2);
20045
+ instance = await WebAssembly.instantiate(module, {
20046
+ ...options.imports,
20047
+ wasi_snapshot_preview1: host2.wasiImport,
20048
+ wasi_unstable: host2.wasiUnstableImport
20049
+ });
20050
+ } catch (error) {
20051
+ ctx.warn(describeStartupFailure(error));
20052
+ return EXIT_NOT_EXECUTABLE;
20053
+ }
20054
+ try {
20055
+ host2.bind(instance);
20056
+ } catch (error) {
20057
+ ctx.warn(error instanceof Error ? error.message : String(error));
20058
+ return EXIT_NOT_EXECUTABLE;
20059
+ }
20060
+ const start2 = instance.exports._start ?? instance.exports._initialize;
20061
+ if (typeof start2 !== "function") {
20062
+ ctx.warn("not a WASI command: the module exports neither _start nor _initialize");
20063
+ return EXIT_NOT_EXECUTABLE;
20064
+ }
20065
+ try {
20066
+ start2();
20067
+ return host2.exitCode ?? 0;
20068
+ } catch (error) {
20069
+ if (error instanceof WasiExit) return error.code;
20070
+ ctx.warn(error instanceof Error ? error.message : String(error));
20071
+ return 134;
20072
+ } finally {
20073
+ try {
20074
+ host2.flushAll();
20075
+ } catch (error) {
20076
+ ctx.warn(`failed to flush open files: ${error instanceof Error ? error.message : String(error)}`);
20077
+ }
20078
+ }
20079
+ }
20080
+ async function compile(bytes2) {
20081
+ const key = await digest(bytes2);
20082
+ const hit = compiled2.get(key);
20083
+ if (hit) {
20084
+ compiled2.delete(key);
20085
+ compiled2.set(key, hit);
20086
+ return hit;
20087
+ }
20088
+ const module = await WebAssembly.compile(sliceForWasm(bytes2));
20089
+ compiled2.set(key, module);
20090
+ if (compiled2.size > MAX_CACHED_MODULES) {
20091
+ const oldest = compiled2.keys().next().value;
20092
+ if (oldest !== void 0) compiled2.delete(oldest);
20093
+ }
20094
+ return module;
20095
+ }
20096
+ async function digest(bytes2) {
20097
+ const hash = await crypto.subtle.digest("SHA-256", sliceForWasm(bytes2));
20098
+ return Array.from(new Uint8Array(hash, 0, 16), (byte) => byte.toString(16).padStart(2, "0")).join("");
20099
+ }
20100
+ function sliceForWasm(bytes2) {
20101
+ return bytes2.buffer.slice(bytes2.byteOffset, bytes2.byteOffset + bytes2.byteLength);
20102
+ }
20103
+ async function primeStdin(ctx) {
20104
+ let pending = new Uint8Array(0);
20105
+ if (!ctx.stdin.isTTY) {
20106
+ try {
20107
+ pending = await ctx.stdin.readAll();
20108
+ } catch {
20109
+ pending = new Uint8Array(0);
20110
+ }
20111
+ }
20112
+ return {
20113
+ read(size) {
20114
+ const chunk = pending.slice(0, Math.min(size, pending.length));
20115
+ pending = pending.subarray(chunk.length);
20116
+ return chunk;
20117
+ },
20118
+ get available() {
20119
+ return pending.length;
20120
+ },
20121
+ isTTY: ctx.stdin.isTTY
20122
+ };
20123
+ }
20124
+ function describeStartupFailure(error) {
20125
+ const message = error instanceof Error ? error.message : String(error);
20126
+ if (error instanceof WebAssembly.LinkError) {
20127
+ return `${message} \u2014 the module needs imports this container does not provide. Build it for wasm32-wasi (a plain wasm32-unknown-unknown or emscripten build will not run here).`;
20128
+ }
20129
+ return message;
20130
+ }
20131
+
20132
+ // src/wasi/command.ts
20133
+ var wasi = defineCommand({
20134
+ name: "wasi",
20135
+ path: "/usr/bin/wasi",
20136
+ summary: "run a WebAssembly (wasm32-wasi) binary",
20137
+ usage: "wasi [--dir PATH] [--mapdir GUEST=PATH] [--env K=V] MODULE.wasm [args...]",
20138
+ manual: `Runs a WebAssembly module built for wasm32-wasi as a container
20139
+ process: it sees the container's filesystem, environment and standard streams,
20140
+ and can be piped and redirected like any other command.
20141
+
20142
+ By default the module is given the whole filesystem, which is what an ordinary
20143
+ program compiled by clang, Rust, Zig or TinyGo expects. Passing --dir or
20144
+ --mapdir replaces that default with exactly what is listed, confining the
20145
+ module to those directories.
20146
+
20147
+ A WASI program has no working directory: wasi-libc fixes its own at / and
20148
+ resolves relative paths from there, exactly as it does under wasmtime. Pass
20149
+ absolute paths, or preopen a directory with --mapdir to give the program a
20150
+ name for it.
20151
+
20152
+ Executable .wasm files are dispatched here automatically, so ./tool.wasm runs
20153
+ without naming this command.
20154
+
20155
+ Sockets are not available: this container's network is an HTTP router rather
20156
+ than a packet path, and socket calls report ENOTSUP.`,
20157
+ async run(ctx) {
20158
+ const preopens = {};
20159
+ const env2 = {};
20160
+ let module;
20161
+ let rest = [];
20162
+ const args = ctx.args;
20163
+ for (let index = 0; index < args.length; index++) {
20164
+ const arg = args[index];
20165
+ if (arg === "--help" || arg === "-h") {
20166
+ ctx.line(wasi.usage);
20167
+ return 0;
20168
+ }
20169
+ if (arg === "--") {
20170
+ module = args[index + 1];
20171
+ rest = args.slice(index + 2);
20172
+ break;
20173
+ }
20174
+ if (arg === "--dir") {
20175
+ const dir3 = args[++index];
20176
+ if (dir3 === void 0) return ctx.reportError(new UsageError("--dir needs a path"));
20177
+ preopens[ctx.path(dir3)] = ctx.path(dir3);
20178
+ continue;
20179
+ }
20180
+ if (arg === "--mapdir") {
20181
+ const pair = args[++index];
20182
+ const split2 = pair?.indexOf("=") ?? -1;
20183
+ if (pair === void 0 || split2 < 0) {
20184
+ return ctx.reportError(new UsageError("--mapdir needs GUEST=PATH"));
20185
+ }
20186
+ preopens[pair.slice(0, split2)] = ctx.path(pair.slice(split2 + 1));
20187
+ continue;
20188
+ }
20189
+ if (arg === "--env") {
20190
+ const pair = args[++index];
20191
+ const split2 = pair?.indexOf("=") ?? -1;
20192
+ if (pair === void 0 || split2 < 0) {
20193
+ return ctx.reportError(new UsageError("--env needs KEY=VALUE"));
20194
+ }
20195
+ env2[pair.slice(0, split2)] = pair.slice(split2 + 1);
20196
+ continue;
20197
+ }
20198
+ module = arg;
20199
+ rest = args.slice(index + 1);
20200
+ break;
20201
+ }
20202
+ if (module === void 0) return ctx.reportError(new UsageError("a module is required"));
20203
+ const abs = ctx.path(module);
20204
+ let bytes2;
20205
+ try {
20206
+ bytes2 = ctx.vfs.readFile(abs, ctx.cred);
20207
+ } catch (error) {
20208
+ ctx.reportError(error, abs);
20209
+ return EXIT_NOT_FOUND;
20210
+ }
20211
+ if (!isWasmBinary(bytes2)) {
20212
+ return ctx.fail(`${abs}: not a WebAssembly module`, 126);
20213
+ }
20214
+ Object.assign(ctx.env, env2);
20215
+ return await runWasi(ctx, bytes2, {
20216
+ /* argv[0] is the path as the user wrote it, not the resolved one: a
20217
+ * program printing its own usage should echo what was typed. */
20218
+ argv: [module, ...rest],
20219
+ ...Object.keys(preopens).length > 0 ? { preopens } : {}
20220
+ });
20221
+ }
20222
+ });
20223
+ function wasiCommands() {
20224
+ return [wasi];
20225
+ }
19128
20226
  var platformBuffer = globalThis.Buffer;
19129
20227
  var Buffer2 = platformBuffer ?? addBase64UrlSupport(Buffer$1);
19130
20228
  function addBase64UrlSupport(BufferClass) {
@@ -19315,8 +20413,8 @@ function verifyIntegrity(data, integrity, shasum, identity) {
19315
20413
  const dash = choice.indexOf("-");
19316
20414
  const algorithm = choice.slice(0, dash);
19317
20415
  const expected = choice.slice(dash + 1).replace(/\?.*$/, "");
19318
- const digest = algorithm === "sha512" ? sha512(data) : algorithm === "sha256" ? sha256(data) : null;
19319
- if (digest && Buffer2.from(digest).toString("base64") === expected) return;
20416
+ const digest2 = algorithm === "sha512" ? sha512(data) : algorithm === "sha256" ? sha256(data) : null;
20417
+ if (digest2 && Buffer2.from(digest2).toString("base64") === expected) return;
19320
20418
  }
19321
20419
  throw new Error(`Integrity check failed for ${identity}`);
19322
20420
  }
@@ -20286,6 +21384,7 @@ function allCommands() {
20286
21384
  ...nodeCommands(),
20287
21385
  ...pythonCommands(),
20288
21386
  ...ffmpegCommands(),
21387
+ ...wasiCommands(),
20289
21388
  ...packageCommands()
20290
21389
  ];
20291
21390
  }
@@ -20445,15 +21544,15 @@ var Session = class {
20445
21544
  async run(command, opts = {}) {
20446
21545
  if (this.closed) throw new Error("session is closed");
20447
21546
  const combined = [];
20448
- const decoder8 = new TextDecoder();
21547
+ const decoder9 = new TextDecoder();
20449
21548
  const stdout = new BufferSink((chunk) => {
20450
- const text2 = decoder8.decode(chunk, { stream: true });
21549
+ const text2 = decoder9.decode(chunk, { stream: true });
20451
21550
  combined.push(text2);
20452
21551
  opts.onStdout?.(text2);
20453
21552
  this.hooks.onStdout?.(text2);
20454
21553
  });
20455
21554
  const stderr = new BufferSink((chunk) => {
20456
- const text2 = decoder8.decode(chunk, { stream: true });
21555
+ const text2 = decoder9.decode(chunk, { stream: true });
20457
21556
  combined.push(text2);
20458
21557
  opts.onStderr?.(text2);
20459
21558
  this.hooks.onStderr?.(text2);
@@ -22907,8 +24006,8 @@ function createCryptoModule() {
22907
24006
  queueMicrotask(() => done?.(null, target));
22908
24007
  },
22909
24008
  hash(algorithm, data, encoding = "hex") {
22910
- const digest = Buffer2.from(hashFor(algorithm)(toBytes2(data)));
22911
- return encoding === "buffer" ? digest : digest.toString(encoding);
24009
+ const digest2 = Buffer2.from(hashFor(algorithm)(toBytes2(data)));
24010
+ return encoding === "buffer" ? digest2 : digest2.toString(encoding);
22912
24011
  },
22913
24012
  timingSafeEqual(a, b) {
22914
24013
  if (a.length !== b.length) throw new RangeError("Input buffers must have the same byte length");
@@ -23910,6 +25009,7 @@ function createCoreModules(options) {
23910
25009
  zlib: zlib_module_default
23911
25010
  };
23912
25011
  for (const name of stubNames) builtins[name] = createUnsupportedModule(name);
25012
+ builtins.net = createNetModule();
23913
25013
  const globals = {
23914
25014
  Buffer: Buffer2,
23915
25015
  console: consoleObject,
@@ -24738,15 +25838,43 @@ function createStreamPromises() {
24738
25838
  })
24739
25839
  };
24740
25840
  }
24741
- function createUnsupportedModule(name) {
25841
+ function createUnsupportedModule(name, supported = {}) {
24742
25842
  const unsupported = () => {
24743
25843
  const error = new Error(`${name} is unavailable in the browser runtime`);
24744
25844
  error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
24745
25845
  throw error;
24746
25846
  };
24747
- return new Proxy({ unsupported }, { get(target, key) {
24748
- return key in target ? target[key] : unsupported;
24749
- } });
25847
+ const target = { unsupported, ...supported };
25848
+ const module = new Proxy(target, {
25849
+ get(object, key) {
25850
+ if (key === "default") return module;
25851
+ return key in object ? object[key] : unsupported;
25852
+ }
25853
+ });
25854
+ return module;
25855
+ }
25856
+ function createNetModule() {
25857
+ const isIPv4 = (value) => {
25858
+ const parts = String(value).split(".");
25859
+ return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255 && String(Number(part)) === part);
25860
+ };
25861
+ const isIPv6 = (value) => {
25862
+ const text2 = String(value);
25863
+ if (!text2.includes(":")) return false;
25864
+ const [head2, tail2] = [text2.slice(0, text2.lastIndexOf(":")), text2.slice(text2.lastIndexOf(":") + 1)];
25865
+ const normalised = isIPv4(tail2) ? `${head2}:0:0` : text2;
25866
+ const halves = normalised.split("::");
25867
+ if (halves.length > 2) return false;
25868
+ const groups = halves.map((half) => half === "" ? [] : half.split(":"));
25869
+ if (groups.some((half) => half.some((group) => !/^[0-9a-fA-F]{1,4}$/.test(group)))) return false;
25870
+ const count = groups.reduce((total, half) => total + half.length, 0);
25871
+ return halves.length === 2 ? count <= 7 : count === 8;
25872
+ };
25873
+ return createUnsupportedModule("net", {
25874
+ isIPv4,
25875
+ isIPv6,
25876
+ isIP: (value) => isIPv4(value) ? 4 : isIPv6(value) ? 6 : 0
25877
+ });
24750
25878
  }
24751
25879
  var stubNames = ["cluster", "dgram", "diagnostics_channel", "domain", "http2", "inspector", "net", "tls", "v8", "vm", "worker_threads"];
24752
25880
  var builtinNames2 = [
@@ -24843,7 +25971,7 @@ var MemoryStat = class {
24843
25971
  return this.inode.kind === "symlink";
24844
25972
  }
24845
25973
  };
24846
- var encoder6 = new TextEncoder();
25974
+ var encoder7 = new TextEncoder();
24847
25975
  var MemoryVolume = class {
24848
25976
  entries = /* @__PURE__ */ new Map();
24849
25977
  nextIno = 2;
@@ -24861,7 +25989,7 @@ var MemoryVolume = class {
24861
25989
  writeFileSync(path, data) {
24862
25990
  const key = this.key(path);
24863
25991
  this.requireParent(key, "open");
24864
- const bytes2 = typeof data === "string" ? encoder6.encode(data) : data.slice();
25992
+ const bytes2 = typeof data === "string" ? encoder7.encode(data) : data.slice();
24865
25993
  const current = this.entries.get(key);
24866
25994
  if (current?.kind === "directory") throw new VolumeError("EISDIR", "open", key);
24867
25995
  if (current?.kind === "symlink") throw new VolumeError("EINVAL", "open", key);
@@ -24874,7 +26002,7 @@ var MemoryVolume = class {
24874
26002
  }
24875
26003
  appendFileSync(path, data) {
24876
26004
  const key = this.key(path);
24877
- const bytes2 = typeof data === "string" ? encoder6.encode(data) : data;
26005
+ const bytes2 = typeof data === "string" ? encoder7.encode(data) : data;
24878
26006
  const current = this.entries.get(key);
24879
26007
  if (!current) return this.writeFileSync(key, bytes2);
24880
26008
  if (current.kind !== "file") throw new VolumeError(current.kind === "directory" ? "EISDIR" : "EINVAL", "open", key);
@@ -25012,7 +26140,7 @@ var MemoryVolume = class {
25012
26140
  if (node2.kind === "directory") directoryCount++;
25013
26141
  else {
25014
26142
  fileCount++;
25015
- totalBytes += node2.kind === "file" ? node2.data.length : encoder6.encode(node2.target).length;
26143
+ totalBytes += node2.kind === "file" ? node2.data.length : encoder7.encode(node2.target).length;
25016
26144
  }
25017
26145
  }
25018
26146
  return { totalBytes, fileCount, directoryCount, dirCount: directoryCount };
@@ -25155,6 +26283,77 @@ var MirroringVolume = class {
25155
26283
  };
25156
26284
  copy(this.root);
25157
26285
  }
26286
+ /**
26287
+ * Bring back what the mirrored engine wrote.
26288
+ *
26289
+ * The mirror exists because Rolldown's resolver reads through WASI rather
26290
+ * than through anything JavaScript can hand it — but Rolldown also *writes*
26291
+ * through WASI, so `vite build` leaves its output in the mirror and the
26292
+ * container sees an empty `dist/`. This is the return leg, and it is why the
26293
+ * mirror is no longer strictly one-way.
26294
+ *
26295
+ * Called when a process ends rather than on a timer: that is the moment a
26296
+ * build's output is complete, and a dev server — which serves from memory
26297
+ * and writes nothing — pays for it only once, at exit.
26298
+ */
26299
+ absorb() {
26300
+ const mirror = this.mirror;
26301
+ if (!mirror?.readdirSync || !mirror.readFileSync || !mirror.lstatSync) return;
26302
+ const visit = (path) => {
26303
+ if (basename(path) === "node_modules") return;
26304
+ let stat2;
26305
+ try {
26306
+ stat2 = mirror.lstatSync(path);
26307
+ } catch {
26308
+ return;
26309
+ }
26310
+ if (stat2.isSymbolicLink()) return;
26311
+ if (stat2.isDirectory()) {
26312
+ let names;
26313
+ try {
26314
+ names = mirror.readdirSync(path);
26315
+ } catch {
26316
+ return;
26317
+ }
26318
+ for (const name of names) visit(join(path, name));
26319
+ return;
26320
+ }
26321
+ let produced;
26322
+ try {
26323
+ produced = mirror.readFileSync(path);
26324
+ } catch {
26325
+ return;
26326
+ }
26327
+ try {
26328
+ const current = this.inner.readFileSync(path);
26329
+ if (sameBytes(current, produced)) return;
26330
+ } catch {
26331
+ }
26332
+ try {
26333
+ this.ensureDirectory(dirname(path));
26334
+ this.inner.writeFileSync(path, produced);
26335
+ } catch {
26336
+ }
26337
+ };
26338
+ visit(this.root);
26339
+ }
26340
+ /** `mkdir -p`, which the volume does not offer directly. */
26341
+ ensureDirectory(path) {
26342
+ const parts = clean(path).split("/").filter(Boolean);
26343
+ let current = "";
26344
+ for (const part of parts) {
26345
+ current += `/${part}`;
26346
+ try {
26347
+ if (this.inner.lstatSync(current).isDirectory()) continue;
26348
+ return;
26349
+ } catch {
26350
+ }
26351
+ try {
26352
+ this.inner.mkdirSync(current);
26353
+ } catch {
26354
+ }
26355
+ }
26356
+ }
25158
26357
  /** Is this path inside the mirrored subtree? */
25159
26358
  mirrored(path) {
25160
26359
  if (!this.mirror) return false;
@@ -25286,6 +26485,11 @@ var MirroringVolume = class {
25286
26485
  this.seed();
25287
26486
  }
25288
26487
  };
26488
+ function sameBytes(a, b) {
26489
+ if (a.length !== b.length) return false;
26490
+ for (let index = 0; index < a.length; index++) if (a[index] !== b[index]) return false;
26491
+ return true;
26492
+ }
25289
26493
 
25290
26494
  // src/runtime/host-esbuild.ts
25291
26495
  var ASYNC_API = ["transform", "build", "context", "formatMessages", "analyzeMetafile"];
@@ -25354,12 +26558,142 @@ function ensureProcessGlobal() {
25354
26558
  configurable: true
25355
26559
  });
25356
26560
  }
26561
+ var BINDING_PACKAGE = "@rolldown/binding-wasm32-wasi";
26562
+ var ASYNC_WORK_POOL_SIZE = 4;
26563
+ var WORKER_FILE = "rolldown-wasi-worker.js";
26564
+ async function loadNodeRolldownMemfsBinding() {
26565
+ const require2 = createRequire(import.meta.url);
26566
+ let packageDir;
26567
+ try {
26568
+ packageDir = dirname$1(require2.resolve(`${BINDING_PACKAGE}/package.json`));
26569
+ } catch {
26570
+ return null;
26571
+ }
26572
+ const fromBinding = createRequire(join$1(packageDir, "package.json"));
26573
+ let runtime;
26574
+ let fsModuleUrl;
26575
+ let createContext2;
26576
+ try {
26577
+ runtime = fromBinding("@napi-rs/wasm-runtime");
26578
+ fsModuleUrl = resolveSubpath(fromBinding, "@napi-rs/wasm-runtime", "./fs");
26579
+ ({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
26580
+ } catch {
26581
+ return null;
26582
+ }
26583
+ const { memfs } = await import(fsModuleUrl);
26584
+ const { Worker: Worker2 } = await import('worker_threads');
26585
+ const { fs: memoryFs, vol } = memfs();
26586
+ const wasi2 = new runtime.WASI({
26587
+ version: "preview1",
26588
+ fs: memoryFs,
26589
+ preopens: { "/": "/" }
26590
+ });
26591
+ const wasmFile = readFileSync(join$1(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
26592
+ const sharedMemory = new WebAssembly.Memory({ initial: 16384, maximum: 65536, shared: true });
26593
+ const workerPoolSize = Math.max(2, cpuCount());
26594
+ const context = createContext2({ autoDestroy: false });
26595
+ context.suppressDestroy();
26596
+ const workerPath = resolveWorkerPath();
26597
+ if (!workerPath) return null;
26598
+ const runtimeAnchor = pathToFileURL$1(join$1(packageDir, "package.json")).href;
26599
+ const workers = /* @__PURE__ */ new Set();
26600
+ let started = false;
26601
+ const { napiModule } = await runtime.instantiateNapiModule(wasmFile, {
26602
+ context,
26603
+ asyncWorkPoolSize: ASYNC_WORK_POOL_SIZE,
26604
+ reuseWorker: { size: ASYNC_WORK_POOL_SIZE + workerPoolSize },
26605
+ plugins: [runtime.emnapiAsyncWorkPlugin, runtime.emnapiTSFNPlugin],
26606
+ wasi: wasi2,
26607
+ onCreateWorker() {
26608
+ const worker = new Worker2(workerPath, {
26609
+ env: process.env,
26610
+ workerData: { runtimeAnchor, fsModuleUrl },
26611
+ /*
26612
+ * Deliberately no `execArgv`.
26613
+ *
26614
+ * A worker inherits the parent's flags by default, and this worker is
26615
+ * a plain JavaScript file we ship — it needs none of them. Inheriting
26616
+ * them is actively harmful: under a host started with a module loader
26617
+ * (`tsx`, `ts-node`, `--import`), the worker's module resolution then
26618
+ * needs the main thread, which is at that moment blocked waiting for
26619
+ * this very worker to report itself ready. The result is a deadlock at
26620
+ * startup with no error, and it took a while to find.
26621
+ */
26622
+ execArgv: []
26623
+ });
26624
+ workers.add(worker);
26625
+ const serveFsRequest = runtime.createOnMessage(memoryFs);
26626
+ worker.on("message", (data) => serveFsRequest({ data }));
26627
+ worker.on("exit", () => workers.delete(worker));
26628
+ if (started) unreferenceWorker(worker);
26629
+ return worker;
26630
+ },
26631
+ overwriteImports(importObject) {
26632
+ importObject.env = {
26633
+ ...importObject.env,
26634
+ ...importObject.napi,
26635
+ ...importObject.emnapi,
26636
+ memory: sharedMemory
26637
+ };
26638
+ return importObject;
26639
+ },
26640
+ beforeInit({ instance }) {
26641
+ for (const name of Object.keys(instance.exports)) {
26642
+ if (name.startsWith("__napi_register__")) instance.exports[name]();
26643
+ }
26644
+ }
26645
+ });
26646
+ started = true;
26647
+ for (const worker of workers) unreferenceWorker(worker);
26648
+ return { ...napiModule.exports, __fs: memoryFs, __volume: vol };
26649
+ }
26650
+ function resolveWorkerPath() {
26651
+ const here = dirname$1(fileURLToPath$1(import.meta.url));
26652
+ const candidates = [
26653
+ join$1(here, WORKER_FILE),
26654
+ /* src/runtime → dist, for a checkout that has been built. */
26655
+ join$1(here, "..", "..", "dist", WORKER_FILE)
26656
+ ];
26657
+ return candidates.find((candidate) => existsSync(candidate)) ?? null;
26658
+ }
26659
+ function resolveSubpath(require2, packageName, subpath) {
26660
+ const packageDir = dirname$1(require2.resolve(packageName));
26661
+ const manifest = JSON.parse(readFileSync(join$1(packageDir, "package.json"), "utf8"));
26662
+ const entry = manifest?.exports?.[subpath];
26663
+ const target = typeof entry === "string" ? entry : entry?.import ?? entry?.default;
26664
+ if (typeof target !== "string") {
26665
+ throw new Error(`${packageName} does not export ${subpath}`);
26666
+ }
26667
+ return pathToFileURL$1(join$1(packageDir, target)).href;
26668
+ }
26669
+ function cpuCount() {
26670
+ try {
26671
+ return process.availableParallelism?.() ?? 4;
26672
+ } catch {
26673
+ return 4;
26674
+ }
26675
+ }
26676
+ function unreferenceWorker(worker) {
26677
+ for (const key of Object.getOwnPropertySymbols(worker)) {
26678
+ if (key.toString().includes("kPublicPort") || key.toString().includes("kHandle")) {
26679
+ const handle = worker[key];
26680
+ if (handle && typeof handle === "object") handle.ref = () => {
26681
+ };
26682
+ }
26683
+ }
26684
+ worker.unref();
26685
+ }
25357
26686
 
25358
26687
  // src/runtime/host-rolldown.ts
25359
26688
  var BUNDLER_HINT = "If this is a bundler pre-bundling the worker away, exclude the binding from dependency optimisation \u2014 in Vite:\n\n optimizeDeps: { exclude: ['@rolldown/binding-wasm32-wasi'] }";
26689
+ var isNodeHost = typeof process !== "undefined" && Boolean(process.versions?.node);
25360
26690
  async function loadHostRolldownBinding() {
25361
26691
  const reason = syncChannelUnavailableReason();
25362
26692
  if (reason) throw new Error(`SandboxedJs threaded WASM unavailable: ${reason}`);
26693
+ if (isNodeHost) {
26694
+ const binding = await loadNodeRolldownMemfsBinding();
26695
+ if (binding) return binding;
26696
+ }
25363
26697
  try {
25364
26698
  const loaded = await import('@rolldown/binding-wasm32-wasi');
25365
26699
  return "__fs" in loaded ? { ...loaded } : loaded.default ?? loaded;
@@ -25759,6 +27093,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
25759
27093
  core.cancelTimers();
25760
27094
  untrack();
25761
27095
  this.router.closeOwner(owner);
27096
+ this.volume.absorb();
25762
27097
  }
25763
27098
  });
25764
27099
  this.running.add(process2);
@@ -25894,10 +27229,10 @@ function attachRolldownMirror(binding, volume, root) {
25894
27229
  }
25895
27230
 
25896
27231
  // src/runtime/remote-volume.ts
25897
- var encoder7 = new TextEncoder();
25898
- var decoder7 = new TextDecoder();
27232
+ var encoder8 = new TextEncoder();
27233
+ var decoder8 = new TextDecoder();
25899
27234
  function encodeFrame(header, body) {
25900
- const json = encoder7.encode(JSON.stringify(header));
27235
+ const json = encoder8.encode(JSON.stringify(header));
25901
27236
  const frame = new Uint8Array(4 + json.length + (body?.length ?? 0));
25902
27237
  new DataView(frame.buffer).setUint32(0, json.length, true);
25903
27238
  frame.set(json, 4);
@@ -25907,7 +27242,7 @@ function encodeFrame(header, body) {
25907
27242
  function decodeFrame(frame) {
25908
27243
  const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
25909
27244
  const headerLength = view.getUint32(0, true);
25910
- const header = JSON.parse(decoder7.decode(frame.subarray(4, 4 + headerLength)));
27245
+ const header = JSON.parse(decoder8.decode(frame.subarray(4, 4 + headerLength)));
25911
27246
  return { header, body: frame.subarray(4 + headerLength) };
25912
27247
  }
25913
27248
  function flattenStat(stat2) {
@@ -26710,15 +28045,15 @@ var Container = class _Container {
26710
28045
  }
26711
28046
  makeStdio(opts) {
26712
28047
  const combined = [];
26713
- const decoder8 = new TextDecoder();
28048
+ const decoder9 = new TextDecoder();
26714
28049
  const stdout = new BufferSink((chunk) => {
26715
- const text2 = decoder8.decode(chunk, { stream: true });
28050
+ const text2 = decoder9.decode(chunk, { stream: true });
26716
28051
  combined.push(text2);
26717
28052
  opts.onStdout?.(text2);
26718
28053
  this.hooks.onStdout?.(text2);
26719
28054
  });
26720
28055
  const stderr = new BufferSink((chunk) => {
26721
- const text2 = decoder8.decode(chunk, { stream: true });
28056
+ const text2 = decoder9.decode(chunk, { stream: true });
26722
28057
  combined.push(text2);
26723
28058
  opts.onStderr?.(text2);
26724
28059
  this.hooks.onStderr?.(text2);
@@ -27422,6 +28757,6 @@ async function renderInto(box, element, options = { port: 80 }) {
27422
28757
  // src/index.ts
27423
28758
  var src_default = createContainer;
27424
28759
 
27425
- export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
28760
+ export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WasiError, WasiExit, WasiHost, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, isWasmBinary, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, runWasi, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo, wasi as wasiCommand };
27426
28761
  //# sourceMappingURL=index.js.map
27427
28762
  //# sourceMappingURL=index.js.map