sandboxedjs 0.1.47 → 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index_js = require('buffer/index.js');
6
5
  var pako = require('pako');
6
+ var index_js = require('buffer/index.js');
7
7
  var sha256 = require('@noble/hashes/sha256');
8
8
  var sha512 = require('@noble/hashes/sha512');
9
9
  var sha1 = require('@noble/hashes/sha1');
@@ -21,10 +21,6 @@ var timersModule = require('timers-browserify');
21
21
  var legacyUrl = require('url/url.js');
22
22
  var legacy$1 = require('@noble/hashes/legacy');
23
23
  var hmac = require('@noble/hashes/hmac');
24
- var module$1 = require('module');
25
- var fs = require('fs');
26
- var path = require('path');
27
- var url = require('url');
28
24
 
29
25
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
30
26
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -3611,10 +3607,10 @@ function builtinType({ shell, argv, io }) {
3611
3607
  }
3612
3608
  async function builtinCommand({ shell, argv, io }) {
3613
3609
  const args = argv.slice(1);
3614
- const describe2 = args.includes("-v") || args.includes("-V");
3610
+ const describe3 = args.includes("-v") || args.includes("-V");
3615
3611
  const verbose = args.includes("-V");
3616
3612
  const rest = args.filter((a) => a !== "-v" && a !== "-V" && a !== "-p");
3617
- if (describe2) {
3613
+ if (describe3) {
3618
3614
  let status = 0;
3619
3615
  for (const name of rest) {
3620
3616
  if (isBuiltinName(name)) {
@@ -13927,15 +13923,15 @@ var sort = defineCommand({
13927
13923
  const slice = fields.slice(start2, end === void 0 ? void 0 : end);
13928
13924
  return slice.join(separator ?? " ");
13929
13925
  };
13930
- const normalize3 = (line) => {
13926
+ const normalize4 = (line) => {
13931
13927
  let value = keys.length ? fieldOf(line, keys[0]) : line;
13932
13928
  if (args.has("ignore-leading-blanks")) value = value.replace(/^\s+/, "");
13933
13929
  if (args.has("ignore-case")) value = value.toLowerCase();
13934
13930
  return value;
13935
13931
  };
13936
13932
  const compare = (a, b) => {
13937
- const x = normalize3(a);
13938
- const y = normalize3(b);
13933
+ const x = normalize4(a);
13934
+ const y = normalize4(b);
13939
13935
  if (args.has("numeric-sort") || args.has("general-numeric-sort")) {
13940
13936
  const nx = parseFloat(x) || 0;
13941
13937
  const ny = parseFloat(y) || 0;
@@ -15176,7 +15172,7 @@ var diff = defineCommand({
15176
15172
  if (a === void 0 || b === void 0) return ctx.fail("missing operand", 2);
15177
15173
  const { sources, ok: ok2 } = await readInputs(ctx, [a, b]);
15178
15174
  if (!ok2 || sources.length < 2) return 2;
15179
- const normalize3 = (line) => {
15175
+ const normalize4 = (line) => {
15180
15176
  let value = line;
15181
15177
  if (args.has("ignore-all-space")) value = value.replace(/\s+/g, "");
15182
15178
  else if (args.has("ignore-space-change")) value = value.replace(/\s+/g, " ").trim();
@@ -15185,7 +15181,7 @@ var diff = defineCommand({
15185
15181
  };
15186
15182
  const left = splitLines(sources[0].bytes).lines;
15187
15183
  const right = splitLines(sources[1].bytes).lines;
15188
- const script = diffLines(left.map(normalize3), right.map(normalize3));
15184
+ const script = diffLines(left.map(normalize4), right.map(normalize4));
15189
15185
  if (script.length === 0) return 0;
15190
15186
  if (args.has("brief")) {
15191
15187
  ctx.line(`Files ${a} and ${b} differ`);
@@ -17685,150 +17681,2167 @@ function nodeCommands() {
17685
17681
  // src/runtime/python.ts
17686
17682
  init_path();
17687
17683
 
17688
- // src/runtime/emscripten-fs.ts
17689
- init_errno();
17690
- init_path();
17691
- var EM_ERRNO = {
17692
- EPERM: 63,
17693
- ENOENT: 44,
17694
- EIO: 29,
17695
- EBADF: 8,
17696
- EACCES: 2,
17697
- EBUSY: 10,
17698
- EEXIST: 20,
17699
- EXDEV: 75,
17700
- ENODEV: 43,
17701
- ENOTDIR: 54,
17702
- EISDIR: 31,
17703
- EINVAL: 28,
17704
- ENFILE: 41,
17705
- EMFILE: 33,
17706
- ENOSPC: 51,
17707
- EROFS: 57,
17708
- ENOTEMPTY: 55,
17709
- ELOOP: 32,
17710
- ENOSYS: 52,
17711
- ENOTSUP: 138
17684
+ // src/runtime/python/host-abi.ts
17685
+ var SBX_HOST_ABI_VERSION = 1;
17686
+ var SBX_REQUEST_HEADER_BYTES = 16;
17687
+ var SBX_RESPONSE_HEADER_BYTES = 20;
17688
+ var Op = {
17689
+ handshake: 0,
17690
+ openat: 1,
17691
+ close: 2,
17692
+ read: 3,
17693
+ write: 4,
17694
+ pread: 5,
17695
+ pwrite: 6,
17696
+ lseek: 7,
17697
+ fstat: 8,
17698
+ statat: 9,
17699
+ ftruncate: 10,
17700
+ renameat: 11,
17701
+ unlinkat: 12,
17702
+ mkdirat: 13,
17703
+ readlinkat: 14,
17704
+ symlinkat: 15,
17705
+ getdents: 16,
17706
+ fsync: 17,
17707
+ dup: 256,
17708
+ dup2: 257,
17709
+ get_flags: 258,
17710
+ set_flags: 259,
17711
+ pipe: 512,
17712
+ poll: 768,
17713
+ clock_gettime: 1024,
17714
+ sleep: 1025,
17715
+ getpid: 1280,
17716
+ getcwd: 1281,
17717
+ chdir: 1282,
17718
+ environ: 1283,
17719
+ getrandom: 1536
17712
17720
  };
17713
- function emErrno(code) {
17714
- return EM_ERRNO[code] ?? EM_ERRNO.EIO;
17721
+ var Errno = {
17722
+ EPERM: 1,
17723
+ ENOENT: 2,
17724
+ ESRCH: 3,
17725
+ EINTR: 4,
17726
+ EIO: 5,
17727
+ ENXIO: 6,
17728
+ E2BIG: 7,
17729
+ EBADF: 9,
17730
+ EAGAIN: 11,
17731
+ ENOMEM: 12,
17732
+ EACCES: 13,
17733
+ EFAULT: 14,
17734
+ EBUSY: 16,
17735
+ EEXIST: 17,
17736
+ EXDEV: 18,
17737
+ ENODEV: 19,
17738
+ ENOTDIR: 20,
17739
+ EISDIR: 21,
17740
+ EINVAL: 22,
17741
+ ENFILE: 23,
17742
+ EMFILE: 24,
17743
+ ENOTTY: 25,
17744
+ EFBIG: 27,
17745
+ ENOSPC: 28,
17746
+ ESPIPE: 29,
17747
+ EROFS: 30,
17748
+ EMLINK: 31,
17749
+ EPIPE: 32,
17750
+ ERANGE: 34,
17751
+ ENAMETOOLONG: 36,
17752
+ ENOSYS: 38,
17753
+ ENOTEMPTY: 39,
17754
+ ELOOP: 40,
17755
+ ENODATA: 61,
17756
+ EPROTO: 71,
17757
+ EOVERFLOW: 75,
17758
+ ETIMEDOUT: 110,
17759
+ ECANCELED: 125,
17760
+ EDQUOT: 122
17761
+ };
17762
+ var CAPABILITIES = ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy", "sockets", "processes", "signals", "storage", "services", "threads"];
17763
+ var OP_NAMES = Object.fromEntries(
17764
+ Object.entries(Op).map(([name, code]) => [code, name])
17765
+ );
17766
+ Object.fromEntries(
17767
+ Object.entries(Errno).map(([name, code]) => [code, name])
17768
+ );
17769
+
17770
+ // src/runtime/python/manifest.ts
17771
+ var MANIFEST_FORMAT = "sandboxedjs-python-runtime";
17772
+ var MANIFEST_SCHEMA_VERSION = 1;
17773
+ var ManifestError = class extends Error {
17774
+ code = "ERR_SBX_PYTHON_MANIFEST";
17775
+ };
17776
+ function validateManifest(value) {
17777
+ const manifest = value;
17778
+ if (!manifest || typeof manifest !== "object") throw new ManifestError("the Python runtime manifest is not an object");
17779
+ if (manifest.format !== MANIFEST_FORMAT) {
17780
+ throw new ManifestError(`not a ${MANIFEST_FORMAT} manifest (format: ${String(manifest.format)})`);
17781
+ }
17782
+ if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {
17783
+ throw new ManifestError(
17784
+ `manifest schema version ${String(manifest.schemaVersion)}, this build understands ${MANIFEST_SCHEMA_VERSION}`
17785
+ );
17786
+ }
17787
+ if (manifest.engine !== "cpython-wasm") {
17788
+ throw new ManifestError(`unknown Python engine ${String(manifest.engine)}`);
17789
+ }
17790
+ if (!manifest.runtimeId) throw new ManifestError("the manifest has no runtimeId");
17791
+ if (!manifest.artifacts?.moduleUrl) throw new ManifestError("the manifest names no module artifact");
17792
+ const abi = manifest.hostAbi;
17793
+ if (!abi || abi.name !== "sbx_host_v1") {
17794
+ throw new ManifestError(`the runtime was built against host ABI ${String(abi?.name)}, not sbx_host_v1`);
17795
+ }
17796
+ if (abi.version !== SBX_HOST_ABI_VERSION) {
17797
+ throw new ManifestError(
17798
+ `the runtime was built against host ABI version ${abi.version}; this kernel speaks ${SBX_HOST_ABI_VERSION}`
17799
+ );
17800
+ }
17801
+ if (!manifest.capabilities) throw new ManifestError("the manifest declares no capabilities");
17802
+ return manifest;
17715
17803
  }
17716
- function mountContainerFs(FS, opts) {
17717
- const { vfs, cred } = opts;
17718
- const throwErrno = (code) => {
17719
- throw new FS.ErrnoError(emErrno(code));
17720
- };
17721
- const translate = (e) => {
17722
- if (isSysError(e)) return throwErrno(e.code);
17723
- throw new FS.ErrnoError(emErrno("EIO"));
17724
- };
17725
- const mountRoot = opts.mountAt ? normalize(opts.mountAt) : "/";
17726
- const backend = {
17727
- /** Absolute container path for a node. */
17728
- realPath(node2) {
17729
- const parts = [];
17730
- let current = node2;
17731
- while (current && current.parent !== current) {
17732
- parts.unshift(current.name);
17733
- current = current.parent;
17734
- }
17735
- const relative2 = parts.join("/");
17736
- if (mountRoot === "/") return "/" + relative2;
17737
- return relative2 ? join(mountRoot, relative2) : mountRoot;
17738
- },
17739
- mount(_mount) {
17740
- return backend.createNode(null, "/", 16877 | 0, 0);
17741
- },
17742
- createNode(parent, name, mode, rdev) {
17743
- const node2 = FS.createNode(parent, name, mode, rdev);
17744
- node2.node_ops = nodeOps;
17745
- node2.stream_ops = streamOps;
17746
- return node2;
17804
+
17805
+ // src/runtime/python/config.ts
17806
+ var bundledManifest = {
17807
+ format: "sandboxedjs-python-runtime",
17808
+ schemaVersion: 1,
17809
+ runtimeId: "sbx-cpython-3.13.5-threaded-fixed",
17810
+ engine: "cpython-wasm",
17811
+ pythonVersion: "3.13.5",
17812
+ profile: "threaded-fixed",
17813
+ hostAbi: { name: "sbx_host_v1", version: 1 },
17814
+ artifacts: {
17815
+ moduleUrl: new URL(
17816
+ (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)).includes("/src/runtime/python/") ? "../../../dist/python/python.js" : "./python/python.js",
17817
+ (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))
17818
+ ).href
17819
+ },
17820
+ capabilities: {
17821
+ threads: true,
17822
+ nativeExtensions: "fixed",
17823
+ networking: "none",
17824
+ processes: "none",
17825
+ persistence: "memory"
17826
+ }
17827
+ };
17828
+ var config = { backend: "sbx-cpython-wasm", manifest: bundledManifest };
17829
+ function setPythonBackend(options) {
17830
+ if (options.manifest !== void 0) config.manifest = validateManifest(options.manifest);
17831
+ if (options.workerUrl !== void 0) config.workerUrl = options.workerUrl;
17832
+ if (options.backend !== void 0) {
17833
+ if (options.backend === "sbx-cpython-wasm" && !config.manifest) {
17834
+ throw new Error(
17835
+ "the sbx-cpython-wasm backend needs a runtime manifest; pass one to configurePython({ manifest })"
17836
+ );
17747
17837
  }
17748
- };
17749
- const nodeOps = {
17750
- getattr(node2) {
17751
- const path = backend.realPath(node2);
17752
- try {
17753
- const st = vfs.lstat(path);
17754
- return {
17755
- dev: st.dev,
17756
- ino: st.ino,
17757
- mode: st.mode,
17758
- nlink: st.nlink,
17759
- uid: st.uid,
17760
- gid: st.gid,
17761
- rdev: st.rdev,
17762
- size: st.size,
17763
- atime: new Date(st.atimeMs),
17764
- mtime: new Date(st.mtimeMs),
17765
- ctime: new Date(st.ctimeMs),
17766
- blksize: 4096,
17767
- blocks: Math.ceil(st.size / 512)
17768
- };
17769
- } catch (e) {
17770
- return translate(e);
17771
- }
17772
- },
17773
- setattr(node2, attr) {
17774
- const path = backend.realPath(node2);
17775
- try {
17776
- if (attr.mode !== void 0) {
17777
- vfs.chmod(path, attr.mode & 4095, cred);
17778
- node2.mode = attr.mode;
17779
- }
17780
- if (attr.timestamp !== void 0) vfs.utimes(path, attr.timestamp, attr.timestamp, cred);
17781
- if (attr.size !== void 0) {
17782
- vfs.truncate(path, attr.size, cred);
17783
- opts.onWrite?.(path);
17784
- }
17785
- } catch (e) {
17786
- translate(e);
17787
- }
17788
- },
17789
- lookup(parent, name) {
17790
- const path = join(backend.realPath(parent), name);
17791
- let mode;
17792
- try {
17793
- mode = vfs.lstat(path).mode;
17794
- } catch {
17795
- throw new FS.ErrnoError(EM_ERRNO.ENOENT);
17796
- }
17797
- return backend.createNode(parent, name, mode, 0);
17798
- },
17799
- mknod(parent, name, mode, dev) {
17800
- const path = join(backend.realPath(parent), name);
17801
- try {
17802
- if (FS.isDir(mode)) {
17803
- vfs.mkdir(path, { cred, mode: mode & 4095 });
17804
- } else if (FS.isFile(mode)) {
17805
- vfs.writeFile(path, new Uint8Array(0), { cred, mode: mode & 4095 });
17806
- } else {
17807
- return throwErrno("ENOTSUP");
17808
- }
17809
- opts.onWrite?.(path);
17810
- } catch (e) {
17811
- translate(e);
17812
- }
17813
- return backend.createNode(parent, name, mode, dev);
17814
- },
17815
- rename(oldNode, newDir, newName) {
17816
- const from = backend.realPath(oldNode);
17817
- const to = join(backend.realPath(newDir), newName);
17838
+ config.backend = options.backend;
17839
+ }
17840
+ }
17841
+ function pythonBackend() {
17842
+ return config;
17843
+ }
17844
+
17845
+ // src/kernel/open-file.ts
17846
+ var PosixError = class extends Error {
17847
+ constructor(errno, message) {
17848
+ super(message ?? `errno ${errno}`);
17849
+ this.errno = errno;
17850
+ this.name = "PosixError";
17851
+ }
17852
+ errno;
17853
+ };
17854
+ var O_RDONLY = 0;
17855
+ var O_WRONLY = 1;
17856
+ var O_CREAT2 = 64;
17857
+ var O_EXCL2 = 128;
17858
+ var O_TRUNC2 = 512;
17859
+ var O_APPEND = 1024;
17860
+ var O_NONBLOCK = 2048;
17861
+ var O_DIRECTORY2 = 65536;
17862
+ var O_CLOEXEC = 524288;
17863
+ function accessMode(flags) {
17864
+ return flags & 3;
17865
+ }
17866
+ var SEEK_SET = 0;
17867
+ var SEEK_CUR = 1;
17868
+ var Inode = class {
17869
+ constructor(ino, path, vfs, cred) {
17870
+ this.ino = ino;
17871
+ this.path = path;
17872
+ this.vfs = vfs;
17873
+ this.cred = cred;
17874
+ }
17875
+ ino;
17876
+ path;
17877
+ vfs;
17878
+ cred;
17879
+ detached = null;
17880
+ refs = 0;
17881
+ /** Read the whole file, wherever it currently lives. */
17882
+ contents() {
17883
+ if (this.detached) return this.detached;
17884
+ try {
17885
+ return this.vfs.readFile(this.path, this.cred);
17886
+ } catch (error) {
17887
+ throw toPosixError(error);
17888
+ }
17889
+ }
17890
+ replace(data) {
17891
+ if (this.detached) {
17892
+ this.detached = data;
17893
+ return;
17894
+ }
17895
+ try {
17896
+ this.vfs.writeFile(this.path, data, { cred: this.cred });
17897
+ } catch (error) {
17898
+ throw toPosixError(error);
17899
+ }
17900
+ }
17901
+ /** Called when the last name is about to be removed. */
17902
+ detach() {
17903
+ if (this.detached) return;
17904
+ try {
17905
+ this.detached = this.vfs.readFile(this.path, this.cred);
17906
+ } catch {
17907
+ this.detached = new Uint8Array();
17908
+ }
17909
+ }
17910
+ size() {
17911
+ return this.contents().length;
17912
+ }
17913
+ stat() {
17914
+ if (this.detached) {
17915
+ return {
17916
+ ino: this.ino,
17917
+ mode: 33188,
17918
+ size: this.detached.length,
17919
+ uid: this.cred.uid,
17920
+ gid: this.cred.gid,
17921
+ nlink: 0,
17922
+ atimeMs: Date.now(),
17923
+ mtimeMs: Date.now(),
17924
+ ctimeMs: Date.now()
17925
+ };
17926
+ }
17927
+ try {
17928
+ const st = this.vfs.stat(this.path, { cred: this.cred });
17929
+ return {
17930
+ ino: st.ino,
17931
+ mode: st.mode,
17932
+ size: st.size,
17933
+ uid: st.uid,
17934
+ gid: st.gid,
17935
+ nlink: st.nlink ?? 1,
17936
+ atimeMs: st.atimeMs,
17937
+ mtimeMs: st.mtimeMs,
17938
+ ctimeMs: st.ctimeMs
17939
+ };
17940
+ } catch (error) {
17941
+ throw toPosixError(error);
17942
+ }
17943
+ }
17944
+ };
17945
+ var FileDescription = class {
17946
+ constructor(inode, flags) {
17947
+ this.inode = inode;
17948
+ this.flags = flags;
17949
+ inode.refs += 1;
17950
+ }
17951
+ inode;
17952
+ flags;
17953
+ kind = "file";
17954
+ offset = 0;
17955
+ read(length) {
17956
+ if (accessMode(this.flags) === O_WRONLY) throw new PosixError(Errno.EBADF);
17957
+ const data = this.inode.contents();
17958
+ const start2 = Math.min(this.offset, data.length);
17959
+ const end = Math.min(start2 + length, data.length);
17960
+ this.offset = end;
17961
+ return data.slice(start2, end);
17962
+ }
17963
+ pread(length, offset) {
17964
+ if (accessMode(this.flags) === O_WRONLY) throw new PosixError(Errno.EBADF);
17965
+ const data = this.inode.contents();
17966
+ const start2 = Math.min(offset, data.length);
17967
+ return data.slice(start2, Math.min(start2 + length, data.length));
17968
+ }
17969
+ write(data) {
17970
+ if (accessMode(this.flags) === O_RDONLY) throw new PosixError(Errno.EBADF);
17971
+ const at = this.flags & O_APPEND ? this.inode.size() : this.offset;
17972
+ const written = this.writeAt(data, at);
17973
+ this.offset = at + written;
17974
+ return written;
17975
+ }
17976
+ pwrite(data, offset) {
17977
+ if (accessMode(this.flags) === O_RDONLY) throw new PosixError(Errno.EBADF);
17978
+ return this.writeAt(data, offset);
17979
+ }
17980
+ writeAt(data, offset) {
17981
+ const current = this.inode.contents();
17982
+ const end = offset + data.length;
17983
+ const next = new Uint8Array(Math.max(current.length, end));
17984
+ next.set(current, 0);
17985
+ next.set(data, offset);
17986
+ this.inode.replace(next);
17987
+ return data.length;
17988
+ }
17989
+ seek(offset, whence) {
17990
+ const base2 = whence === SEEK_SET ? 0 : whence === SEEK_CUR ? this.offset : this.inode.size();
17991
+ const next = base2 + offset;
17992
+ if (next < 0) throw new PosixError(Errno.EINVAL);
17993
+ this.offset = next;
17994
+ return next;
17995
+ }
17996
+ truncate(length) {
17997
+ const current = this.inode.contents();
17998
+ const next = new Uint8Array(length);
17999
+ next.set(current.subarray(0, Math.min(length, current.length)), 0);
18000
+ this.inode.replace(next);
18001
+ }
18002
+ stat() {
18003
+ return this.inode.stat();
18004
+ }
18005
+ readable() {
18006
+ return true;
18007
+ }
18008
+ writable() {
18009
+ return true;
18010
+ }
18011
+ whenReady() {
18012
+ return Promise.resolve();
18013
+ }
18014
+ close() {
18015
+ this.inode.refs -= 1;
18016
+ if (this.inode.refs <= 0) this.inode.detached = null;
18017
+ }
18018
+ };
18019
+ var DirectoryDescription = class {
18020
+ constructor(path, flags, vfs, cred) {
18021
+ this.path = path;
18022
+ this.flags = flags;
18023
+ this.vfs = vfs;
18024
+ this.cred = cred;
18025
+ }
18026
+ path;
18027
+ flags;
18028
+ vfs;
18029
+ cred;
18030
+ kind = "dir";
18031
+ entries = null;
18032
+ index = 0;
18033
+ /** Entry names, NUL-terminated, as many as fit in `length`. */
18034
+ read(length) {
18035
+ if (!this.entries) {
17818
18036
  try {
17819
- vfs.rename(from, to, cred);
17820
- opts.onWrite?.(to);
17821
- } catch (e) {
17822
- translate(e);
18037
+ this.entries = [".", "..", ...this.vfs.readdir(this.path, this.cred)];
18038
+ } catch (error) {
18039
+ throw toPosixError(error);
17823
18040
  }
17824
- oldNode.name = newName;
17825
- oldNode.parent = newDir;
17826
- },
17827
- unlink(parent, name) {
17828
- const path = join(backend.realPath(parent), name);
17829
- try {
17830
- vfs.unlink(path, cred);
17831
- opts.onWrite?.(path);
18041
+ }
18042
+ const encoder9 = new TextEncoder();
18043
+ const parts = [];
18044
+ let total = 0;
18045
+ while (this.index < this.entries.length) {
18046
+ const encoded = encoder9.encode(this.entries[this.index] + "\0");
18047
+ if (total + encoded.length > length) break;
18048
+ parts.push(encoded);
18049
+ total += encoded.length;
18050
+ this.index += 1;
18051
+ }
18052
+ const joined = new Uint8Array(total);
18053
+ let at = 0;
18054
+ for (const part of parts) {
18055
+ joined.set(part, at);
18056
+ at += part.length;
18057
+ }
18058
+ return joined;
18059
+ }
18060
+ write() {
18061
+ throw new PosixError(Errno.EISDIR);
18062
+ }
18063
+ seek(offset, whence) {
18064
+ if (whence === SEEK_SET && offset === 0) {
18065
+ this.index = 0;
18066
+ return 0;
18067
+ }
18068
+ throw new PosixError(Errno.EINVAL);
18069
+ }
18070
+ stat() {
18071
+ try {
18072
+ const st = this.vfs.stat(this.path, { cred: this.cred });
18073
+ return {
18074
+ ino: st.ino,
18075
+ mode: st.mode,
18076
+ size: st.size,
18077
+ uid: st.uid,
18078
+ gid: st.gid,
18079
+ nlink: st.nlink ?? 2,
18080
+ atimeMs: st.atimeMs,
18081
+ mtimeMs: st.mtimeMs,
18082
+ ctimeMs: st.ctimeMs
18083
+ };
18084
+ } catch (error) {
18085
+ throw toPosixError(error);
18086
+ }
18087
+ }
18088
+ readable() {
18089
+ return true;
18090
+ }
18091
+ writable() {
18092
+ return false;
18093
+ }
18094
+ whenReady() {
18095
+ return Promise.resolve();
18096
+ }
18097
+ close() {
18098
+ }
18099
+ };
18100
+ var PIPE_CAPACITY = 64 * 1024;
18101
+ var PipeBuffer = class {
18102
+ chunks = [];
18103
+ queued = 0;
18104
+ readers = 0;
18105
+ writers = 0;
18106
+ waiters = [];
18107
+ get available() {
18108
+ return this.queued;
18109
+ }
18110
+ get space() {
18111
+ return Math.max(0, PIPE_CAPACITY - this.queued);
18112
+ }
18113
+ push(data) {
18114
+ this.chunks.push(data);
18115
+ this.queued += data.length;
18116
+ this.wake();
18117
+ }
18118
+ take(length) {
18119
+ const out = new Uint8Array(Math.min(length, this.queued));
18120
+ let at = 0;
18121
+ while (at < out.length) {
18122
+ const head2 = this.chunks[0];
18123
+ const take = Math.min(head2.length, out.length - at);
18124
+ out.set(head2.subarray(0, take), at);
18125
+ at += take;
18126
+ if (take === head2.length) this.chunks.shift();
18127
+ else this.chunks[0] = head2.subarray(take);
18128
+ }
18129
+ this.queued -= out.length;
18130
+ this.wake();
18131
+ return out;
18132
+ }
18133
+ /** Anything that may have changed readiness for either end. */
18134
+ wake() {
18135
+ const waiting = this.waiters;
18136
+ this.waiters = [];
18137
+ for (const resolve2 of waiting) resolve2();
18138
+ }
18139
+ whenReady() {
18140
+ return new Promise((resolve2) => this.waiters.push(resolve2));
18141
+ }
18142
+ };
18143
+ var PipeReadEnd = class {
18144
+ constructor(buffer, flags) {
18145
+ this.buffer = buffer;
18146
+ this.flags = flags;
18147
+ buffer.readers += 1;
18148
+ }
18149
+ buffer;
18150
+ flags;
18151
+ kind = "pipe";
18152
+ read(length) {
18153
+ if (this.buffer.available === 0) {
18154
+ if (this.buffer.writers === 0) return new Uint8Array();
18155
+ throw new PosixError(Errno.EAGAIN);
18156
+ }
18157
+ return this.buffer.take(length);
18158
+ }
18159
+ write() {
18160
+ throw new PosixError(Errno.EBADF);
18161
+ }
18162
+ stat() {
18163
+ return pipeStat();
18164
+ }
18165
+ readable() {
18166
+ return this.buffer.available > 0 || this.buffer.writers === 0;
18167
+ }
18168
+ writable() {
18169
+ return false;
18170
+ }
18171
+ whenReady() {
18172
+ return this.buffer.whenReady();
18173
+ }
18174
+ close() {
18175
+ this.buffer.readers -= 1;
18176
+ this.buffer.wake();
18177
+ }
18178
+ };
18179
+ var PipeWriteEnd = class {
18180
+ constructor(buffer, flags) {
18181
+ this.buffer = buffer;
18182
+ this.flags = flags;
18183
+ buffer.writers += 1;
18184
+ }
18185
+ buffer;
18186
+ flags;
18187
+ kind = "pipe";
18188
+ read() {
18189
+ throw new PosixError(Errno.EBADF);
18190
+ }
18191
+ write(data) {
18192
+ if (this.buffer.readers === 0) throw new PosixError(Errno.EPIPE);
18193
+ const space = this.buffer.space;
18194
+ if (space === 0) throw new PosixError(Errno.EAGAIN);
18195
+ const accepted = data.subarray(0, space);
18196
+ this.buffer.push(accepted.slice());
18197
+ return accepted.length;
18198
+ }
18199
+ stat() {
18200
+ return pipeStat();
18201
+ }
18202
+ readable() {
18203
+ return false;
18204
+ }
18205
+ writable() {
18206
+ return this.buffer.space > 0 || this.buffer.readers === 0;
18207
+ }
18208
+ whenReady() {
18209
+ return this.buffer.whenReady();
18210
+ }
18211
+ close() {
18212
+ this.buffer.writers -= 1;
18213
+ this.buffer.wake();
18214
+ }
18215
+ };
18216
+ function pipeStat() {
18217
+ const now = Date.now();
18218
+ return { ino: 0, mode: 4480, size: 0, uid: 0, gid: 0, nlink: 1, atimeMs: now, mtimeMs: now, ctimeMs: now };
18219
+ }
18220
+ function createPipe(flags = 0) {
18221
+ const buffer = new PipeBuffer();
18222
+ return [new PipeReadEnd(buffer, flags | O_RDONLY), new PipeWriteEnd(buffer, flags | O_WRONLY)];
18223
+ }
18224
+ var StreamDescription = class {
18225
+ constructor(flags, sink, isTty = false) {
18226
+ this.flags = flags;
18227
+ this.sink = sink;
18228
+ this.isTty = isTty;
18229
+ }
18230
+ flags;
18231
+ sink;
18232
+ isTty;
18233
+ kind = "stream";
18234
+ queue = [];
18235
+ queued = 0;
18236
+ ended = false;
18237
+ waiters = [];
18238
+ /** Host side: give the guest more input. */
18239
+ push(data) {
18240
+ if (data.length === 0) return;
18241
+ this.queue.push(data);
18242
+ this.queued += data.length;
18243
+ this.wake();
18244
+ }
18245
+ /** Host side: no more input will arrive. */
18246
+ end() {
18247
+ this.ended = true;
18248
+ this.wake();
18249
+ }
18250
+ read(length) {
18251
+ if (this.queued === 0) {
18252
+ if (this.ended) return new Uint8Array();
18253
+ throw new PosixError(Errno.EAGAIN);
18254
+ }
18255
+ const out = new Uint8Array(Math.min(length, this.queued));
18256
+ let at = 0;
18257
+ while (at < out.length) {
18258
+ const head2 = this.queue[0];
18259
+ const take = Math.min(head2.length, out.length - at);
18260
+ out.set(head2.subarray(0, take), at);
18261
+ at += take;
18262
+ if (take === head2.length) this.queue.shift();
18263
+ else this.queue[0] = head2.subarray(take);
18264
+ }
18265
+ this.queued -= out.length;
18266
+ return out;
18267
+ }
18268
+ write(data) {
18269
+ if (!this.sink) throw new PosixError(Errno.EBADF);
18270
+ this.sink(data);
18271
+ return data.length;
18272
+ }
18273
+ stat() {
18274
+ const now = Date.now();
18275
+ return {
18276
+ ino: 0,
18277
+ mode: this.isTty ? 8592 : 4480,
18278
+ size: 0,
18279
+ uid: 0,
18280
+ gid: 0,
18281
+ nlink: 1,
18282
+ atimeMs: now,
18283
+ mtimeMs: now,
18284
+ ctimeMs: now
18285
+ };
18286
+ }
18287
+ readable() {
18288
+ return this.queued > 0 || this.ended;
18289
+ }
18290
+ writable() {
18291
+ return true;
18292
+ }
18293
+ whenReady() {
18294
+ return new Promise((resolve2) => this.waiters.push(resolve2));
18295
+ }
18296
+ close() {
18297
+ this.wake();
18298
+ }
18299
+ wake() {
18300
+ const waiting = this.waiters;
18301
+ this.waiters = [];
18302
+ for (const resolve2 of waiting) resolve2();
18303
+ }
18304
+ };
18305
+ function toPosixError(error) {
18306
+ if (error instanceof PosixError) return error;
18307
+ const code = error?.code;
18308
+ const message = error?.message;
18309
+ const mapped = {
18310
+ ENOENT: Errno.ENOENT,
18311
+ EEXIST: Errno.EEXIST,
18312
+ ENOTDIR: Errno.ENOTDIR,
18313
+ EISDIR: Errno.EISDIR,
18314
+ EACCES: Errno.EACCES,
18315
+ EPERM: Errno.EPERM,
18316
+ ENOTEMPTY: Errno.ENOTEMPTY,
18317
+ EINVAL: Errno.EINVAL,
18318
+ ELOOP: Errno.ELOOP,
18319
+ EXDEV: Errno.EXDEV,
18320
+ ENOSPC: Errno.ENOSPC,
18321
+ EMFILE: Errno.EMFILE,
18322
+ ENAMETOOLONG: Errno.ENAMETOOLONG,
18323
+ EBADF: Errno.EBADF,
18324
+ EPIPE: Errno.EPIPE
18325
+ };
18326
+ return new PosixError(code && mapped[code] ? mapped[code] : Errno.EIO, message);
18327
+ }
18328
+ var DescriptorTable = class _DescriptorTable {
18329
+ slots = /* @__PURE__ */ new Map();
18330
+ /** Descriptions this table shares with others, so close counts correctly. */
18331
+ static refs = /* @__PURE__ */ new WeakMap();
18332
+ /** Install at a chosen number, closing whatever was there. */
18333
+ set(fd, description, cloexec = false) {
18334
+ if (fd < 0) throw new PosixError(Errno.EBADF);
18335
+ const existing = this.slots.get(fd);
18336
+ if (existing) this.release(existing.description);
18337
+ this.slots.set(fd, { description, cloexec });
18338
+ _DescriptorTable.refs.set(description, (_DescriptorTable.refs.get(description) ?? 0) + 1);
18339
+ return fd;
18340
+ }
18341
+ /** Install at the lowest free number at or above `from`. */
18342
+ add(description, cloexec = false, from = 0) {
18343
+ let fd = from;
18344
+ while (this.slots.has(fd)) fd += 1;
18345
+ return this.set(fd, description, cloexec);
18346
+ }
18347
+ get(fd) {
18348
+ const slot = this.slots.get(fd);
18349
+ if (!slot) throw new PosixError(Errno.EBADF);
18350
+ return slot.description;
18351
+ }
18352
+ has(fd) {
18353
+ return this.slots.has(fd);
18354
+ }
18355
+ cloexec(fd) {
18356
+ const slot = this.slots.get(fd);
18357
+ if (!slot) throw new PosixError(Errno.EBADF);
18358
+ return slot.cloexec;
18359
+ }
18360
+ setCloexec(fd, value) {
18361
+ const slot = this.slots.get(fd);
18362
+ if (!slot) throw new PosixError(Errno.EBADF);
18363
+ slot.cloexec = value;
18364
+ }
18365
+ /** `dup`: a second name for one description, so the offset is shared. */
18366
+ dup(fd, from = 0) {
18367
+ return this.add(this.get(fd), false, from);
18368
+ }
18369
+ dup2(fd, target) {
18370
+ const description = this.get(fd);
18371
+ if (fd === target) return target;
18372
+ return this.set(target, description, false);
18373
+ }
18374
+ close(fd) {
18375
+ const slot = this.slots.get(fd);
18376
+ if (!slot) throw new PosixError(Errno.EBADF);
18377
+ this.slots.delete(fd);
18378
+ this.release(slot.description);
18379
+ }
18380
+ /** Everything a child inherits: a spawn keeps all but the close-on-exec ones. */
18381
+ inherit() {
18382
+ const child = new _DescriptorTable();
18383
+ for (const [fd, slot] of this.slots) {
18384
+ if (slot.cloexec) continue;
18385
+ child.set(fd, slot.description, false);
18386
+ }
18387
+ return child;
18388
+ }
18389
+ /** Drop every descriptor. Every resource has an owner and a cleanup path. */
18390
+ closeAll() {
18391
+ for (const fd of [...this.slots.keys()]) {
18392
+ try {
18393
+ this.close(fd);
18394
+ } catch {
18395
+ }
18396
+ }
18397
+ }
18398
+ get openCount() {
18399
+ return this.slots.size;
18400
+ }
18401
+ release(description) {
18402
+ const refs = (_DescriptorTable.refs.get(description) ?? 1) - 1;
18403
+ if (refs <= 0) {
18404
+ _DescriptorTable.refs.delete(description);
18405
+ description.close();
18406
+ } else {
18407
+ _DescriptorTable.refs.set(description, refs);
18408
+ }
18409
+ }
18410
+ };
18411
+ var FileService = class {
18412
+ constructor(vfs, cred) {
18413
+ this.vfs = vfs;
18414
+ this.cred = cred;
18415
+ }
18416
+ vfs;
18417
+ cred;
18418
+ openInodes = /* @__PURE__ */ new Map();
18419
+ open(path, flags, mode = 438) {
18420
+ const exists = this.vfs.lexists(path);
18421
+ if (flags & O_CREAT2 && flags & O_EXCL2 && exists) throw new PosixError(Errno.EEXIST);
18422
+ if (!exists) {
18423
+ if (!(flags & O_CREAT2)) throw new PosixError(Errno.ENOENT);
18424
+ if (accessMode(flags) === O_RDONLY) throw new PosixError(Errno.EACCES);
18425
+ try {
18426
+ this.vfs.writeFile(path, new Uint8Array(), { cred: this.cred, mode });
18427
+ } catch (error) {
18428
+ throw toPosixError(error);
18429
+ }
18430
+ }
18431
+ let stat2;
18432
+ try {
18433
+ stat2 = this.vfs.stat(path, { cred: this.cred });
18434
+ } catch (error) {
18435
+ throw toPosixError(error);
18436
+ }
18437
+ if (stat2.isDirectory()) {
18438
+ if (accessMode(flags) !== O_RDONLY) throw new PosixError(Errno.EISDIR);
18439
+ return new DirectoryDescription(path, flags, this.vfs, this.cred);
18440
+ }
18441
+ if (flags & O_DIRECTORY2) throw new PosixError(Errno.ENOTDIR);
18442
+ const inode = this.inodeFor(stat2.ino, path);
18443
+ const description = new FileDescription(inode, flags);
18444
+ if (flags & O_TRUNC2 && accessMode(flags) !== O_RDONLY) description.truncate(0);
18445
+ return description;
18446
+ }
18447
+ /**
18448
+ * Remove a name.
18449
+ *
18450
+ * If something still has the file open, its contents move into the open
18451
+ * description first: POSIX guarantees a reader keeps reading, and a volume
18452
+ * that only knows about paths cannot provide that on its own.
18453
+ */
18454
+ unlink(path) {
18455
+ let ino = null;
18456
+ try {
18457
+ ino = this.vfs.lstat(path).ino;
18458
+ } catch (error) {
18459
+ throw toPosixError(error);
18460
+ }
18461
+ const inode = ino === null ? void 0 : this.openInodes.get(ino);
18462
+ if (inode && inode.refs > 0 && inode.path === path) inode.detach();
18463
+ try {
18464
+ this.vfs.unlink(path, this.cred);
18465
+ } catch (error) {
18466
+ throw toPosixError(error);
18467
+ }
18468
+ if (inode && inode.refs <= 0) this.openInodes.delete(ino);
18469
+ }
18470
+ /** Rename a name. Anything already open follows the file to its new name. */
18471
+ rename(from, to) {
18472
+ let ino = null;
18473
+ try {
18474
+ ino = this.vfs.lstat(from).ino;
18475
+ } catch {
18476
+ }
18477
+ try {
18478
+ this.vfs.rename(from, to, this.cred);
18479
+ } catch (error) {
18480
+ throw toPosixError(error);
18481
+ }
18482
+ if (ino === null) return;
18483
+ const inode = this.openInodes.get(ino);
18484
+ if (inode) inode.path = to;
18485
+ }
18486
+ /** One {@link Inode} per file identity, shared by every open description. */
18487
+ inodeFor(ino, path) {
18488
+ const existing = this.openInodes.get(ino);
18489
+ if (existing) {
18490
+ existing.path = path;
18491
+ return existing;
18492
+ }
18493
+ const created = new Inode(ino, path, this.vfs, this.cred);
18494
+ this.openInodes.set(ino, created);
18495
+ return created;
18496
+ }
18497
+ };
18498
+
18499
+ // src/runtime/python/protocol.ts
18500
+ var ProtocolError = class extends Error {
18501
+ code = "ERR_SBX_ABI_PROTOCOL";
18502
+ };
18503
+ function decodeRequest(frame) {
18504
+ if (frame.length < SBX_REQUEST_HEADER_BYTES) throw new ProtocolError("short request frame");
18505
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
18506
+ const header = {
18507
+ version: view.getUint16(0, true),
18508
+ op: view.getUint16(2, true),
18509
+ requestId: view.getUint32(4, true),
18510
+ generation: view.getUint32(8, true)
18511
+ };
18512
+ const length = view.getUint32(12, true);
18513
+ if (frame.length - SBX_REQUEST_HEADER_BYTES < length) {
18514
+ throw new ProtocolError(`truncated payload for ${OP_NAMES[header.op] ?? header.op}`);
18515
+ }
18516
+ return {
18517
+ header,
18518
+ payload: frame.subarray(SBX_REQUEST_HEADER_BYTES, SBX_REQUEST_HEADER_BYTES + length)
18519
+ };
18520
+ }
18521
+ function encodeResponse(header, status, payload) {
18522
+ const frame = new Uint8Array(SBX_RESPONSE_HEADER_BYTES + payload.length);
18523
+ const view = new DataView(frame.buffer);
18524
+ view.setUint16(0, header.version, true);
18525
+ view.setUint16(2, header.op, true);
18526
+ view.setUint32(4, header.requestId, true);
18527
+ view.setUint32(8, header.generation, true);
18528
+ view.setInt32(12, status, true);
18529
+ view.setUint32(16, payload.length, true);
18530
+ frame.set(payload, SBX_RESPONSE_HEADER_BYTES);
18531
+ return frame;
18532
+ }
18533
+ var Reader = class {
18534
+ at = 0;
18535
+ view;
18536
+ bytes;
18537
+ constructor(bytes2) {
18538
+ this.bytes = bytes2;
18539
+ this.view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
18540
+ }
18541
+ need(count) {
18542
+ if (this.at + count > this.bytes.length) throw new ProtocolError("payload underrun");
18543
+ const offset = this.at;
18544
+ this.at += count;
18545
+ return offset;
18546
+ }
18547
+ i32() {
18548
+ return this.view.getInt32(this.need(4), true);
18549
+ }
18550
+ u32() {
18551
+ return this.view.getUint32(this.need(4), true);
18552
+ }
18553
+ i64() {
18554
+ return Number(this.view.getBigInt64(this.need(8), true));
18555
+ }
18556
+ u64() {
18557
+ return Number(this.view.getBigUint64(this.need(8), true));
18558
+ }
18559
+ bytes32() {
18560
+ const length = this.u32();
18561
+ const offset = this.need(length);
18562
+ return this.bytes.subarray(offset, offset + length);
18563
+ }
18564
+ string() {
18565
+ return new TextDecoder().decode(this.bytes32());
18566
+ }
18567
+ get remaining() {
18568
+ return this.bytes.length - this.at;
18569
+ }
18570
+ };
18571
+ var Writer = class {
18572
+ parts = [];
18573
+ i32(value) {
18574
+ const b = new Uint8Array(4);
18575
+ new DataView(b.buffer).setInt32(0, value, true);
18576
+ this.parts.push(b);
18577
+ return this;
18578
+ }
18579
+ u32(value) {
18580
+ const b = new Uint8Array(4);
18581
+ new DataView(b.buffer).setUint32(0, value >>> 0, true);
18582
+ this.parts.push(b);
18583
+ return this;
18584
+ }
18585
+ i64(value) {
18586
+ const b = new Uint8Array(8);
18587
+ new DataView(b.buffer).setBigInt64(0, BigInt(Math.trunc(value)), true);
18588
+ this.parts.push(b);
18589
+ return this;
18590
+ }
18591
+ u64(value) {
18592
+ const b = new Uint8Array(8);
18593
+ new DataView(b.buffer).setBigUint64(0, BigInt(Math.trunc(value)), true);
18594
+ this.parts.push(b);
18595
+ return this;
18596
+ }
18597
+ bytes32(value) {
18598
+ this.u32(value.length);
18599
+ this.parts.push(value);
18600
+ return this;
18601
+ }
18602
+ string(value) {
18603
+ return this.bytes32(new TextEncoder().encode(value));
18604
+ }
18605
+ finish() {
18606
+ const total = this.parts.reduce((sum, part) => sum + part.length, 0);
18607
+ const out = new Uint8Array(total);
18608
+ let at = 0;
18609
+ for (const part of this.parts) {
18610
+ out.set(part, at);
18611
+ at += part.length;
18612
+ }
18613
+ return out;
18614
+ }
18615
+ };
18616
+ function failure(errno) {
18617
+ return -Math.abs(errno);
18618
+ }
18619
+
18620
+ // src/runtime/python/syscall-server.ts
18621
+ var MAX_TRANSFER = 8 * 1024 * 1024;
18622
+ function createHostAbiServer(proc) {
18623
+ const files = new FileService(proc.vfs, proc.cred);
18624
+ const started = Date.now();
18625
+ const monotonicBase = typeof performance === "object" ? performance.now() : 0;
18626
+ return async (frame) => {
18627
+ const { header, payload } = decodeRequest(frame);
18628
+ if (header.version !== SBX_HOST_ABI_VERSION) {
18629
+ return encodeResponse(header, failure(Errno.EPROTO), new Uint8Array());
18630
+ }
18631
+ if (header.generation !== proc.generation) {
18632
+ return encodeResponse(header, failure(Errno.ESRCH), new Uint8Array());
18633
+ }
18634
+ try {
18635
+ const { status, payload: out } = await dispatch(header, new Reader(payload));
18636
+ return encodeResponse(header, status, out ?? new Uint8Array());
18637
+ } catch (error) {
18638
+ const posix = toPosixError(error);
18639
+ return encodeResponse(header, failure(posix.errno), new Uint8Array());
18640
+ }
18641
+ };
18642
+ async function dispatch(header, r) {
18643
+ switch (header.op) {
18644
+ case Op.handshake: {
18645
+ const w = new Writer().u32(SBX_HOST_ABI_VERSION).u32(CAPABILITIES.length);
18646
+ for (const capability of CAPABILITIES) {
18647
+ w.string(capability).u32(implemented(capability) ? 1 : 0);
18648
+ }
18649
+ return { status: 0, payload: w.finish() };
18650
+ }
18651
+ /* ------------------------------------------------------------ files */
18652
+ case Op.openat: {
18653
+ const path = resolve2(r.string());
18654
+ const flags = r.u32();
18655
+ const mode = r.u32();
18656
+ const description = files.open(path, flags, mode);
18657
+ return { status: proc.table.add(description, (flags & O_CLOEXEC) !== 0) };
18658
+ }
18659
+ case Op.close: {
18660
+ proc.table.close(r.i32());
18661
+ return { status: 0 };
18662
+ }
18663
+ case Op.read: {
18664
+ const description = proc.table.get(r.i32());
18665
+ const length = clamp(r.u32());
18666
+ const data = await readBlocking(description, length);
18667
+ return { status: data.length, payload: data };
18668
+ }
18669
+ case Op.write: {
18670
+ const description = proc.table.get(r.i32());
18671
+ const data = r.bytes32();
18672
+ const written = await writeBlocking(description, data);
18673
+ return { status: written };
18674
+ }
18675
+ case Op.pread: {
18676
+ const description = proc.table.get(r.i32());
18677
+ const length = clamp(r.u32());
18678
+ const offset = r.u64();
18679
+ if (!description.pread) throw new PosixError(Errno.ESPIPE);
18680
+ const data = description.pread(length, offset);
18681
+ return { status: data.length, payload: data };
18682
+ }
18683
+ case Op.pwrite: {
18684
+ const description = proc.table.get(r.i32());
18685
+ const data = r.bytes32();
18686
+ const offset = r.u64();
18687
+ if (!description.pwrite) throw new PosixError(Errno.ESPIPE);
18688
+ return { status: description.pwrite(data, offset) };
18689
+ }
18690
+ case Op.lseek: {
18691
+ const description = proc.table.get(r.i32());
18692
+ const offset = r.i64();
18693
+ const whence = r.u32();
18694
+ if (!description.seek) throw new PosixError(Errno.ESPIPE);
18695
+ return { status: 0, payload: new Writer().i64(description.seek(offset, whence)).finish() };
18696
+ }
18697
+ case Op.fstat: {
18698
+ return { status: 0, payload: encodeStat(proc.table.get(r.i32()).stat()) };
18699
+ }
18700
+ case Op.statat: {
18701
+ const path = resolve2(r.string());
18702
+ const followLinks = r.u32() !== 0;
18703
+ try {
18704
+ const st = followLinks ? proc.vfs.stat(path, { cred: proc.cred }) : proc.vfs.lstat(path);
18705
+ return {
18706
+ status: 0,
18707
+ payload: encodeStat({
18708
+ ino: st.ino,
18709
+ mode: st.mode,
18710
+ size: st.size,
18711
+ uid: st.uid,
18712
+ gid: st.gid,
18713
+ nlink: st.nlink ?? 1,
18714
+ atimeMs: st.atimeMs,
18715
+ mtimeMs: st.mtimeMs,
18716
+ ctimeMs: st.ctimeMs
18717
+ })
18718
+ };
18719
+ } catch (error) {
18720
+ throw toPosixError(error);
18721
+ }
18722
+ }
18723
+ case Op.ftruncate: {
18724
+ const description = proc.table.get(r.i32());
18725
+ const length = r.u64();
18726
+ if (!description.truncate) throw new PosixError(Errno.EINVAL);
18727
+ description.truncate(length);
18728
+ return { status: 0 };
18729
+ }
18730
+ case Op.renameat: {
18731
+ files.rename(resolve2(r.string()), resolve2(r.string()));
18732
+ return { status: 0 };
18733
+ }
18734
+ case Op.unlinkat: {
18735
+ const path = resolve2(r.string());
18736
+ const removeDirectory = r.u32() !== 0;
18737
+ if (removeDirectory) {
18738
+ try {
18739
+ proc.vfs.rmdir(path, proc.cred);
18740
+ } catch (error) {
18741
+ throw toPosixError(error);
18742
+ }
18743
+ } else {
18744
+ files.unlink(path);
18745
+ }
18746
+ return { status: 0 };
18747
+ }
18748
+ case Op.mkdirat: {
18749
+ const path = resolve2(r.string());
18750
+ const mode = r.u32();
18751
+ try {
18752
+ proc.vfs.mkdir(path, { mode, cred: proc.cred });
18753
+ } catch (error) {
18754
+ throw toPosixError(error);
18755
+ }
18756
+ return { status: 0 };
18757
+ }
18758
+ case Op.readlinkat: {
18759
+ try {
18760
+ const target = proc.vfs.readlink(resolve2(r.string()), proc.cred);
18761
+ return { status: 0, payload: new Writer().string(target).finish() };
18762
+ } catch (error) {
18763
+ throw toPosixError(error);
18764
+ }
18765
+ }
18766
+ case Op.symlinkat: {
18767
+ const target = r.string();
18768
+ try {
18769
+ proc.vfs.symlink(target, resolve2(r.string()), proc.cred);
18770
+ } catch (error) {
18771
+ throw toPosixError(error);
18772
+ }
18773
+ return { status: 0 };
18774
+ }
18775
+ case Op.getdents: {
18776
+ const description = proc.table.get(r.i32());
18777
+ if (description.kind !== "dir") throw new PosixError(Errno.ENOTDIR);
18778
+ const data = description.read(clamp(r.u32()));
18779
+ return { status: data.length, payload: data };
18780
+ }
18781
+ case Op.fsync: {
18782
+ proc.table.get(r.i32());
18783
+ return { status: 0 };
18784
+ }
18785
+ /* ------------------------------------------------------ descriptors */
18786
+ case Op.dup:
18787
+ return { status: proc.table.dup(r.i32(), r.u32()) };
18788
+ case Op.dup2:
18789
+ return { status: proc.table.dup2(r.i32(), r.i32()) };
18790
+ case Op.get_flags: {
18791
+ const fd = r.i32();
18792
+ const description = proc.table.get(fd);
18793
+ return { status: 0, payload: new Writer().u32(description.flags).u32(proc.table.cloexec(fd) ? 1 : 0).finish() };
18794
+ }
18795
+ case Op.set_flags: {
18796
+ const fd = r.i32();
18797
+ const flags = r.u32();
18798
+ const cloexec = r.u32();
18799
+ const description = proc.table.get(fd);
18800
+ description.flags = accessMode(description.flags) | flags & -4;
18801
+ proc.table.setCloexec(fd, cloexec !== 0);
18802
+ return { status: 0 };
18803
+ }
18804
+ /* ------------------------------------------------------------ pipes */
18805
+ case Op.pipe: {
18806
+ const flags = r.u32();
18807
+ const [readEnd, writeEnd] = createPipe(flags & O_NONBLOCK);
18808
+ const cloexec = (flags & O_CLOEXEC) !== 0;
18809
+ const readFd = proc.table.add(readEnd, cloexec);
18810
+ const writeFd = proc.table.add(writeEnd, cloexec);
18811
+ return { status: 0, payload: new Writer().i32(readFd).i32(writeFd).finish() };
18812
+ }
18813
+ /* -------------------------------------------------------- readiness */
18814
+ case Op.poll: {
18815
+ const count = r.u32();
18816
+ const requests = [];
18817
+ for (let i = 0; i < count; i += 1) requests.push({ fd: r.i32(), events: r.u32() });
18818
+ const timeoutMs = r.i64();
18819
+ const ready = await poll(requests, timeoutMs);
18820
+ const w = new Writer().u32(ready.length);
18821
+ for (const entry of ready) w.i32(entry.fd).u32(entry.revents);
18822
+ return { status: ready.length, payload: w.finish() };
18823
+ }
18824
+ /* ------------------------------------------------------------- time */
18825
+ case Op.clock_gettime: {
18826
+ const monotonic = r.u32() !== 0;
18827
+ const now = monotonic ? (typeof performance === "object" ? performance.now() : Date.now() - started) - monotonicBase : Date.now();
18828
+ const nanos = Math.round(now * 1e6);
18829
+ return { status: 0, payload: new Writer().i64(Math.floor(nanos / 1e9)).u32(nanos % 1e9).finish() };
18830
+ }
18831
+ case Op.sleep: {
18832
+ const nanos = r.u64();
18833
+ await sleep(nanos / 1e6);
18834
+ return { status: proc.signal.aborted ? failure(Errno.EINTR) : 0 };
18835
+ }
18836
+ /* --------------------------------------------------------- identity */
18837
+ case Op.getpid: {
18838
+ const w = new Writer().i32(proc.pid).i32(proc.ppid).u32(proc.cred.uid).u32(proc.cred.gid).u32(proc.cred.umask ?? 18);
18839
+ return { status: 0, payload: w.finish() };
18840
+ }
18841
+ case Op.getcwd:
18842
+ return { status: 0, payload: new Writer().string(proc.cwd).finish() };
18843
+ case Op.chdir: {
18844
+ const path = resolve2(r.string());
18845
+ let st;
18846
+ try {
18847
+ st = proc.vfs.stat(path, { cred: proc.cred });
18848
+ } catch (error) {
18849
+ throw toPosixError(error);
18850
+ }
18851
+ if (!st.isDirectory()) throw new PosixError(Errno.ENOTDIR);
18852
+ proc.cwd = path;
18853
+ return { status: 0 };
18854
+ }
18855
+ case Op.environ: {
18856
+ const joined = Object.entries(proc.env).map(([key, value]) => `${key}=${value}`).join("\0");
18857
+ return { status: 0, payload: new Writer().string(joined).finish() };
18858
+ }
18859
+ /* ---------------------------------------------------------- entropy */
18860
+ case Op.getrandom: {
18861
+ const length = Math.min(r.u32(), 65536);
18862
+ const bytes2 = new Uint8Array(length);
18863
+ crypto.getRandomValues(bytes2);
18864
+ return { status: length, payload: bytes2 };
18865
+ }
18866
+ default:
18867
+ return { status: failure(Errno.ENOSYS) };
18868
+ }
18869
+ }
18870
+ function implemented(capability) {
18871
+ return ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy"].includes(capability);
18872
+ }
18873
+ function resolve2(path) {
18874
+ if (path.startsWith("/")) return proc.vfs.resolvePath(path);
18875
+ return proc.vfs.resolvePath(`${proc.cwd.replace(/\/$/, "")}/${path}`);
18876
+ }
18877
+ function clamp(length) {
18878
+ return Math.min(length, MAX_TRANSFER);
18879
+ }
18880
+ async function readBlocking(description, length) {
18881
+ for (; ; ) {
18882
+ try {
18883
+ return description.read(length);
18884
+ } catch (error) {
18885
+ const posix = toPosixError(error);
18886
+ if (posix.errno !== Errno.EAGAIN || description.flags & O_NONBLOCK) throw posix;
18887
+ }
18888
+ await Promise.race([description.whenReady(), aborted()]);
18889
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18890
+ }
18891
+ }
18892
+ async function writeBlocking(description, data) {
18893
+ for (; ; ) {
18894
+ try {
18895
+ return description.write(data);
18896
+ } catch (error) {
18897
+ const posix = toPosixError(error);
18898
+ if (posix.errno !== Errno.EAGAIN || description.flags & O_NONBLOCK) throw posix;
18899
+ }
18900
+ await Promise.race([description.whenReady(), aborted()]);
18901
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18902
+ }
18903
+ }
18904
+ const POLLIN = 1;
18905
+ const POLLOUT = 4;
18906
+ const POLLHUP = 16;
18907
+ async function poll(requests, timeoutMs) {
18908
+ const deadline = timeoutMs < 0 ? Infinity : Date.now() + timeoutMs;
18909
+ for (; ; ) {
18910
+ const ready = [];
18911
+ const waits = [];
18912
+ for (const { fd, events } of requests) {
18913
+ let description;
18914
+ try {
18915
+ description = proc.table.get(fd);
18916
+ } catch {
18917
+ ready.push({ fd, revents: POLLHUP });
18918
+ continue;
18919
+ }
18920
+ let revents = 0;
18921
+ if (events & POLLIN && description.readable()) revents |= POLLIN;
18922
+ if (events & POLLOUT && description.writable()) revents |= POLLOUT;
18923
+ if (revents) ready.push({ fd, revents });
18924
+ else waits.push(description.whenReady());
18925
+ }
18926
+ if (ready.length > 0) return ready;
18927
+ const remaining = deadline - Date.now();
18928
+ if (remaining <= 0) return [];
18929
+ await Promise.race([...waits, sleep(Math.min(remaining, 50)), aborted()]);
18930
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18931
+ }
18932
+ }
18933
+ function aborted() {
18934
+ if (proc.signal.aborted) return Promise.resolve();
18935
+ return new Promise((resolve3) => proc.signal.addEventListener("abort", () => resolve3(), { once: true }));
18936
+ }
18937
+ }
18938
+ function sleep(ms) {
18939
+ return new Promise((resolve2) => setTimeout(resolve2, Math.max(0, ms)));
18940
+ }
18941
+ function encodeStat(st) {
18942
+ return new Writer().u64(st.ino).u32(st.mode).u64(st.size).u32(st.uid).u32(st.gid).u32(st.nlink).i64(Math.round(st.atimeMs * 1e6)).i64(Math.round(st.mtimeMs * 1e6)).i64(Math.round(st.ctimeMs * 1e6)).finish();
18943
+ }
18944
+
18945
+ // src/runtime/python/sync-transport.ts
18946
+ var STATE = 0;
18947
+ var LENGTH = 1;
18948
+ var MORE = 2;
18949
+ var CONTROL_WORDS = 4;
18950
+ var STATE_REQUEST = 1;
18951
+ var STATE_RESPONSE = 2;
18952
+ var STATE_CONTINUE = 3;
18953
+ var STATE_CLOSED = 4;
18954
+ function createHostTransportBuffers(windowBytes = 1 << 20) {
18955
+ return {
18956
+ control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
18957
+ data: new SharedArrayBuffer(windowBytes)
18958
+ };
18959
+ }
18960
+ function syncTransportUnavailableReason() {
18961
+ if (typeof globalThis.crossOriginIsolated === "boolean" && !globalThis.crossOriginIsolated) {
18962
+ return "the page is not cross-origin isolated. Serve it with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.";
18963
+ }
18964
+ if (typeof SharedArrayBuffer !== "function") return "SharedArrayBuffer is unavailable in this host.";
18965
+ if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return "Atomics.wait is unavailable in this host.";
18966
+ return null;
18967
+ }
18968
+ var HostCallServer = class {
18969
+ control;
18970
+ data;
18971
+ capacity;
18972
+ handle;
18973
+ onFault;
18974
+ incoming = [];
18975
+ outgoing = null;
18976
+ sent = 0;
18977
+ closed = false;
18978
+ constructor(buffers, handle, onFault = () => {
18979
+ }) {
18980
+ this.control = new Int32Array(buffers.control);
18981
+ this.data = new Uint8Array(buffers.data);
18982
+ this.capacity = this.data.length;
18983
+ this.handle = handle;
18984
+ this.onFault = onFault;
18985
+ }
18986
+ /** Call on each wake message from the client. */
18987
+ async pump() {
18988
+ if (this.closed) return;
18989
+ const state = Atomics.load(this.control, STATE);
18990
+ if (state === STATE_REQUEST) {
18991
+ const size = Atomics.load(this.control, LENGTH);
18992
+ this.incoming.push(this.data.slice(0, size));
18993
+ if (Atomics.load(this.control, MORE) === 1) {
18994
+ this.publish(STATE_CONTINUE);
18995
+ return;
18996
+ }
18997
+ const request = concat3(this.incoming);
18998
+ this.incoming = [];
18999
+ let response;
19000
+ try {
19001
+ response = await this.handle(request);
19002
+ } catch (error) {
19003
+ this.onFault(error);
19004
+ this.close();
19005
+ return;
19006
+ }
19007
+ if (this.closed) return;
19008
+ this.outgoing = response;
19009
+ this.sent = 0;
19010
+ this.sendWindow();
19011
+ return;
19012
+ }
19013
+ if (state === STATE_CONTINUE) this.sendWindow();
19014
+ }
19015
+ /** Release a blocked client — container teardown, or the worker being killed. */
19016
+ close() {
19017
+ if (this.closed) return;
19018
+ this.closed = true;
19019
+ Atomics.store(this.control, STATE, STATE_CLOSED);
19020
+ Atomics.notify(this.control, STATE);
19021
+ }
19022
+ sendWindow() {
19023
+ const payload = this.outgoing ?? new Uint8Array();
19024
+ const size = Math.min(this.capacity, payload.length - this.sent);
19025
+ this.data.set(payload.subarray(this.sent, this.sent + size), 0);
19026
+ this.sent += size;
19027
+ Atomics.store(this.control, LENGTH, size);
19028
+ Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
19029
+ this.publish(STATE_RESPONSE);
19030
+ }
19031
+ publish(state) {
19032
+ Atomics.store(this.control, STATE, state);
19033
+ Atomics.notify(this.control, STATE);
19034
+ }
19035
+ };
19036
+ function concat3(parts) {
19037
+ if (parts.length === 1) return parts[0];
19038
+ const total = parts.reduce((sum, part) => sum + part.length, 0);
19039
+ const joined = new Uint8Array(total);
19040
+ let at = 0;
19041
+ for (const part of parts) {
19042
+ joined.set(part, at);
19043
+ at += part.length;
19044
+ }
19045
+ return joined;
19046
+ }
19047
+
19048
+ // src/runtime/worker-host.ts
19049
+ function defaultWorkerUrl() {
19050
+ return new URL("./worker-entry.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
19051
+ }
19052
+ function hasDomWorker() {
19053
+ return typeof Worker === "function";
19054
+ }
19055
+ async function startRuntimeWorker(options = {}) {
19056
+ const url = options.url ?? defaultWorkerUrl();
19057
+ const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
19058
+ try {
19059
+ await new Promise((resolve2, reject) => {
19060
+ const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
19061
+ worker.onMessage((message) => {
19062
+ if (message?.type === "sandboxedjs:ready") {
19063
+ clearTimeout(timer);
19064
+ resolve2();
19065
+ }
19066
+ });
19067
+ worker.onError((error) => {
19068
+ clearTimeout(timer);
19069
+ reject(error instanceof Error ? error : new Error(String(error)));
19070
+ });
19071
+ });
19072
+ } catch (error) {
19073
+ try {
19074
+ await worker.terminate();
19075
+ } catch {
19076
+ }
19077
+ throw error;
19078
+ }
19079
+ return worker;
19080
+ }
19081
+ async function startDomWorker(url) {
19082
+ const worker = new Worker(url, { type: "module" });
19083
+ const listeners = [];
19084
+ const errors = [];
19085
+ worker.addEventListener("message", (event) => {
19086
+ for (const listener of listeners) listener(event.data);
19087
+ });
19088
+ worker.addEventListener("error", (event) => {
19089
+ const detail = event.message || "the runtime worker failed to load";
19090
+ for (const listener of errors) listener(new Error(detail));
19091
+ });
19092
+ return {
19093
+ postMessage: (message) => worker.postMessage(message),
19094
+ onMessage: (listener) => {
19095
+ listeners.push(listener);
19096
+ },
19097
+ onError: (listener) => {
19098
+ errors.push(listener);
19099
+ },
19100
+ terminate: () => worker.terminate()
19101
+ };
19102
+ }
19103
+ async function startNodeWorker(url, workerData) {
19104
+ const specifier = ["node", "worker_threads"].join(":");
19105
+ const { Worker: NodeWorker } = await import(
19106
+ /* @vite-ignore */
19107
+ /* webpackIgnore: true */
19108
+ specifier
19109
+ );
19110
+ const worker = new NodeWorker(url, { workerData });
19111
+ return {
19112
+ postMessage: (message) => worker.postMessage(message),
19113
+ onMessage: (listener) => worker.on("message", listener),
19114
+ onError: (listener) => worker.on("error", listener),
19115
+ terminate: () => worker.terminate()
19116
+ };
19117
+ }
19118
+
19119
+ // src/runtime/python/supervisor.ts
19120
+ var nextGeneration = 1;
19121
+ async function startPythonProcess(options) {
19122
+ const unavailable2 = syncTransportUnavailableReason();
19123
+ if (unavailable2) {
19124
+ throw new Error(`The SandboxedJs Python runtime needs blocking worker calls: ${unavailable2}`);
19125
+ }
19126
+ const generation = nextGeneration++;
19127
+ const stdin = new StreamDescription(O_RDONLY, null, options.isTty);
19128
+ const stdout = new StreamDescription(O_WRONLY, options.onStdout, options.isTty);
19129
+ const stderr = new StreamDescription(O_WRONLY, options.onStderr, options.isTty);
19130
+ const table = new DescriptorTable();
19131
+ table.set(0, stdin);
19132
+ table.set(1, stdout);
19133
+ table.set(2, stderr);
19134
+ const controller = new AbortController();
19135
+ const abort = () => controller.abort();
19136
+ options.signal.addEventListener("abort", abort, { once: true });
19137
+ const handle = createHostAbiServer({
19138
+ pid: options.pid,
19139
+ ppid: options.ppid,
19140
+ generation,
19141
+ vfs: options.vfs,
19142
+ cred: options.cred,
19143
+ table,
19144
+ env: options.env,
19145
+ cwd: options.cwd,
19146
+ signal: controller.signal
19147
+ });
19148
+ const buffers = createHostTransportBuffers();
19149
+ let fault = null;
19150
+ const server = new HostCallServer(buffers, handle, (error) => {
19151
+ fault = error;
19152
+ });
19153
+ let worker = null;
19154
+ let settle = () => {
19155
+ };
19156
+ let blame = () => {
19157
+ };
19158
+ const finished = new Promise((resolve2, reject) => {
19159
+ settle = resolve2;
19160
+ blame = reject;
19161
+ });
19162
+ let done = false;
19163
+ const cleanup = () => {
19164
+ if (done) return;
19165
+ done = true;
19166
+ options.signal.removeEventListener("abort", abort);
19167
+ server.close();
19168
+ table.closeAll();
19169
+ try {
19170
+ worker?.terminate();
19171
+ } catch {
19172
+ }
19173
+ };
19174
+ worker = await startRuntimeWorker({ url: options.workerUrl });
19175
+ worker.onMessage((message) => {
19176
+ const detail = message;
19177
+ if (detail?.type === "wake") {
19178
+ void server.pump();
19179
+ return;
19180
+ }
19181
+ if (detail?.type === "exit") {
19182
+ cleanup();
19183
+ settle(detail.code ?? 0);
19184
+ return;
19185
+ }
19186
+ if (detail?.type === "failed") {
19187
+ cleanup();
19188
+ blame(new Error(detail.message ?? "the Python process failed to start"));
19189
+ }
19190
+ });
19191
+ worker.onError((error) => {
19192
+ cleanup();
19193
+ blame(error instanceof Error ? error : new Error(String(error)));
19194
+ });
19195
+ controller.signal.addEventListener("abort", () => {
19196
+ if (done) return;
19197
+ cleanup();
19198
+ settle(130);
19199
+ });
19200
+ worker.postMessage({
19201
+ type: "start",
19202
+ buffers,
19203
+ generation,
19204
+ moduleUrl: options.manifest.artifacts.moduleUrl,
19205
+ argv: options.argv,
19206
+ env: options.env,
19207
+ cwd: options.cwd,
19208
+ pid: options.pid,
19209
+ ppid: options.ppid,
19210
+ mounts: options.mounts,
19211
+ isTty: options.isTty
19212
+ });
19213
+ return {
19214
+ write: (data) => stdin.push(data),
19215
+ endInput: () => stdin.end(),
19216
+ kill: () => controller.abort(),
19217
+ wait: async () => {
19218
+ const code = await finished;
19219
+ if (fault) throw fault;
19220
+ return code;
19221
+ }
19222
+ };
19223
+ }
19224
+ var END_OF_CENTRAL_DIRECTORY = 101010256;
19225
+ var CENTRAL_FILE_HEADER = 33639248;
19226
+ var ZIP64_END_LOCATOR = 117853008;
19227
+ var ZIP64_END_OF_CENTRAL_DIRECTORY = 101075792;
19228
+ var ZipError = class extends Error {
19229
+ code = "ERR_ZIP";
19230
+ };
19231
+ function readZip(archive) {
19232
+ const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
19233
+ const end = findEndOfCentralDirectory(archive);
19234
+ let entryCount = view.getUint16(end + 10, true);
19235
+ let directoryOffset = view.getUint32(end + 16, true);
19236
+ if (entryCount === 65535 || directoryOffset === 4294967295) {
19237
+ const locator = findSignature(archive, ZIP64_END_LOCATOR, end);
19238
+ if (locator < 0) throw new ZipError("zip64 archive without an end locator");
19239
+ const zip64End = Number(view.getBigUint64(locator + 8, true));
19240
+ if (view.getUint32(zip64End, true) !== ZIP64_END_OF_CENTRAL_DIRECTORY) {
19241
+ throw new ZipError("zip64 end record not found where the locator says");
19242
+ }
19243
+ entryCount = Number(view.getBigUint64(zip64End + 32, true));
19244
+ directoryOffset = Number(view.getBigUint64(zip64End + 48, true));
19245
+ }
19246
+ const entries = [];
19247
+ let at = directoryOffset;
19248
+ for (let i = 0; i < entryCount; i += 1) {
19249
+ if (view.getUint32(at, true) !== CENTRAL_FILE_HEADER) {
19250
+ throw new ZipError(`central directory entry ${i} has a bad signature`);
19251
+ }
19252
+ const flags = view.getUint16(at + 8, true);
19253
+ if (flags & 1) throw new ZipError("encrypted archives are not supported");
19254
+ const method = view.getUint16(at + 10, true);
19255
+ const compressedSize = view.getUint32(at + 20, true);
19256
+ const uncompressedSize = view.getUint32(at + 24, true);
19257
+ const nameLength = view.getUint16(at + 28, true);
19258
+ const extraLength = view.getUint16(at + 30, true);
19259
+ const commentLength = view.getUint16(at + 32, true);
19260
+ const externalAttributes = view.getUint32(at + 38, true);
19261
+ const localOffset = view.getUint32(at + 42, true);
19262
+ const name = new TextDecoder().decode(archive.subarray(at + 46, at + 46 + nameLength));
19263
+ entries.push({
19264
+ name,
19265
+ /* The high 16 bits are the Unix mode when the archive was made on Unix.
19266
+ * pip relies on it for console scripts, which have to stay executable. */
19267
+ mode: externalAttributes >>> 16,
19268
+ isDirectory: name.endsWith("/"),
19269
+ data: () => extract(archive, view, localOffset, method, compressedSize, uncompressedSize, name)
19270
+ });
19271
+ at += 46 + nameLength + extraLength + commentLength;
19272
+ }
19273
+ return entries;
19274
+ }
19275
+ function extract(archive, view, localOffset, method, compressedSize, uncompressedSize, name) {
19276
+ const nameLength = view.getUint16(localOffset + 26, true);
19277
+ const extraLength = view.getUint16(localOffset + 28, true);
19278
+ const start2 = localOffset + 30 + nameLength + extraLength;
19279
+ const body = archive.subarray(start2, start2 + compressedSize);
19280
+ if (method === 0) return body.slice();
19281
+ if (method !== 8) throw new ZipError(`${name}: unsupported compression method ${method}`);
19282
+ const inflated = pako.inflateRaw(body);
19283
+ if (inflated.length !== uncompressedSize) {
19284
+ throw new ZipError(`${name}: inflated to ${inflated.length} bytes, the directory says ${uncompressedSize}`);
19285
+ }
19286
+ return inflated;
19287
+ }
19288
+ function findEndOfCentralDirectory(archive, view) {
19289
+ const at = findSignature(archive, END_OF_CENTRAL_DIRECTORY, archive.length);
19290
+ if (at < 0) throw new ZipError("not a zip archive: no end-of-central-directory record");
19291
+ return at;
19292
+ }
19293
+ function findSignature(archive, signature, before) {
19294
+ const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
19295
+ for (let at = Math.min(before, archive.length) - 4; at >= 0; at -= 1) {
19296
+ if (view.getUint32(at, true) === signature) return at;
19297
+ }
19298
+ return -1;
19299
+ }
19300
+
19301
+ // src/runtime/python/pypi.ts
19302
+ var ACCEPTED_TAGS = ["py3-none-any", "py2.py3-none-any", "py3-none-emscripten"];
19303
+ function parseRequirement(text2) {
19304
+ const cleaned = text2.replace(/#.*$/, "").trim();
19305
+ if (!cleaned) return null;
19306
+ const [head2, marker = null] = splitOnce(cleaned, ";");
19307
+ const match2 = /^([A-Za-z0-9._-]+)\s*(\[[^\]]*\])?\s*(.*)$/.exec(head2.trim());
19308
+ if (!match2) return null;
19309
+ const [, name, extrasGroup = "", rest = ""] = match2;
19310
+ const specifiers = [];
19311
+ for (const part of rest.split(",")) {
19312
+ const spec = /^\s*(===|==|>=|<=|!=|~=|>|<)\s*([A-Za-z0-9.*+!-]+)\s*$/.exec(part);
19313
+ if (spec) specifiers.push({ op: spec[1], version: spec[2] });
19314
+ }
19315
+ return {
19316
+ name: normalize2(name),
19317
+ specifiers,
19318
+ extras: extrasGroup.slice(1, -1).split(",").map((e) => e.trim()).filter(Boolean),
19319
+ marker: marker?.trim() ?? null
19320
+ };
19321
+ }
19322
+ function normalize2(name) {
19323
+ return name.toLowerCase().replace(/[-_.]+/g, "-");
19324
+ }
19325
+ function markerApplies(marker, environment, extras) {
19326
+ if (!marker) return true;
19327
+ const evaluateClause = (clause) => {
19328
+ const match2 = /^\s*([A-Za-z_]+)\s*(===|==|!=|>=|<=|>|<|not in|in)\s*(.+?)\s*$/.exec(clause);
19329
+ if (!match2) return false;
19330
+ const [, variable, op, rawValue] = match2;
19331
+ const value = rawValue.replace(/^['"]|['"]$/g, "");
19332
+ if (variable === "extra") {
19333
+ const wanted = extras.includes(value);
19334
+ return op === "==" ? wanted : op === "!=" ? !wanted : false;
19335
+ }
19336
+ const actual = environment[variable];
19337
+ if (actual === void 0) return false;
19338
+ if (op === "in") return value.includes(actual);
19339
+ if (op === "not in") return !value.includes(actual);
19340
+ if (variable === "python_version" || variable === "python_full_version") {
19341
+ return compareVersions(actual, value, op);
19342
+ }
19343
+ return op === "==" || op === "===" ? actual === value : op === "!=" ? actual !== value : false;
19344
+ };
19345
+ if (marker.includes("(")) return false;
19346
+ return marker.split(/\s+or\s+/).some((group) => group.split(/\s+and\s+/).every(evaluateClause));
19347
+ }
19348
+ function compareVersions(left, right, op) {
19349
+ if (op === "==" || op === "===") {
19350
+ if (right.endsWith(".*")) return left.startsWith(right.slice(0, -1));
19351
+ return releaseOf(left).join(".") === releaseOf(right).join(".");
19352
+ }
19353
+ if (op === "!=") return !compareVersions(left, right, "==");
19354
+ const order = compareRelease(releaseOf(left), releaseOf(right));
19355
+ if (op === ">=") return order >= 0;
19356
+ if (op === "<=") return order <= 0;
19357
+ if (op === ">") return order > 0;
19358
+ if (op === "<") return order < 0;
19359
+ if (op === "~=") {
19360
+ const bound = releaseOf(right).slice(0, -1);
19361
+ return order >= 0 && compareRelease(releaseOf(left).slice(0, bound.length), bound) === 0;
19362
+ }
19363
+ return false;
19364
+ }
19365
+ function releaseOf(version) {
19366
+ const release = /^\s*v?(\d+(?:\.\d+)*)/.exec(version.trim());
19367
+ return release ? release[1].split(".").map(Number) : [0];
19368
+ }
19369
+ function compareRelease(left, right) {
19370
+ for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
19371
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
19372
+ if (difference !== 0) return difference > 0 ? 1 : -1;
19373
+ }
19374
+ return 0;
19375
+ }
19376
+ function isPreRelease(version) {
19377
+ return /(a|b|rc|dev|post)\d*$/i.test(version.replace(/^\d+(\.\d+)*/, ""));
19378
+ }
19379
+ async function resolvePackage(client, requirement, environment) {
19380
+ const index = await client.json(`https://pypi.org/pypi/${requirement.name}/json`);
19381
+ const candidates = Object.keys(index.releases).filter((version) => requirement.specifiers.every((s) => compareVersions(version, s.version, s.op))).sort((a, b) => compareRelease(releaseOf(a), releaseOf(b)));
19382
+ const stable = candidates.filter((version) => !isPreRelease(version));
19383
+ const ordered = (stable.length > 0 ? stable : candidates).reverse();
19384
+ let sawSourceOnly = false;
19385
+ for (const version of ordered) {
19386
+ const files = (index.releases[version] ?? []).filter((file3) => !file3.yanked);
19387
+ const wheel = pickWheel(files);
19388
+ if (wheel) {
19389
+ return {
19390
+ name: requirement.name,
19391
+ version,
19392
+ url: wheel.url,
19393
+ filename: wheel.filename,
19394
+ sha256: wheel.digests?.sha256 ?? "",
19395
+ requires: []
19396
+ };
19397
+ }
19398
+ if (files.some((file3) => file3.packagetype === "sdist")) sawSourceOnly = true;
19399
+ }
19400
+ if (ordered.length === 0) {
19401
+ throw new Error(
19402
+ `no version of ${requirement.name} matches ${describe(requirement)}`
19403
+ );
19404
+ }
19405
+ throw new Error(
19406
+ sawSourceOnly ? `${requirement.name} ${ordered[0]} publishes only a source distribution, and this runtime cannot build one yet (source builds are a later milestone)` : `${requirement.name} ${ordered[0]} has no wheel this runtime can use; it needs one tagged ` + ACCEPTED_TAGS.join(" or ")
19407
+ );
19408
+ }
19409
+ function pickWheel(files) {
19410
+ const wheels = files.filter((file3) => file3.packagetype === "bdist_wheel");
19411
+ for (const tag2 of ACCEPTED_TAGS) {
19412
+ const match2 = wheels.find((file3) => file3.filename.endsWith(`-${tag2}.whl`));
19413
+ if (match2) return match2;
19414
+ }
19415
+ return null;
19416
+ }
19417
+ function describe(requirement) {
19418
+ return requirement.specifiers.length === 0 ? "any version" : requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ");
19419
+ }
19420
+ function splitOnce(text2, separator) {
19421
+ const at = text2.indexOf(separator);
19422
+ return at < 0 ? [text2] : [text2.slice(0, at), text2.slice(at + 1)];
19423
+ }
19424
+
19425
+ // src/runtime/python/install.ts
19426
+ var SITE_PACKAGES = "/usr/lib/python3.13/site-packages";
19427
+ function markerEnvironment(pythonVersion) {
19428
+ const [major = "3", minor = "13"] = pythonVersion.split(".");
19429
+ return {
19430
+ python_version: `${major}.${minor}`,
19431
+ python_full_version: pythonVersion,
19432
+ sys_platform: "emscripten",
19433
+ platform_system: "Emscripten",
19434
+ platform_machine: "wasm32",
19435
+ implementation_name: "cpython",
19436
+ os_name: "posix"
19437
+ };
19438
+ }
19439
+ async function installRequirements(options) {
19440
+ const environment = markerEnvironment(options.pythonVersion);
19441
+ const report = { installed: [], skipped: [] };
19442
+ const queue = [];
19443
+ for (const text2 of options.requirements) {
19444
+ const requirement = parseRequirement(text2);
19445
+ if (requirement) queue.push({ requirement, extras: requirement.extras });
19446
+ }
19447
+ const chosen = /* @__PURE__ */ new Map();
19448
+ const seen = /* @__PURE__ */ new Set();
19449
+ while (queue.length > 0) {
19450
+ const { requirement, extras } = queue.shift();
19451
+ if (!markerApplies(requirement.marker, environment, extras)) {
19452
+ report.skipped.push({ name: requirement.name, marker: requirement.marker ?? "" });
19453
+ continue;
19454
+ }
19455
+ const key = `${requirement.name}[${extras.join(",")}]`;
19456
+ if (seen.has(key)) continue;
19457
+ seen.add(key);
19458
+ const already = chosen.get(requirement.name);
19459
+ if (already) {
19460
+ const satisfied = requirement.specifiers.every(
19461
+ (s) => compareVersions(already.version, s.version, s.op)
19462
+ );
19463
+ if (!satisfied) {
19464
+ throw new Error(
19465
+ `${requirement.name} ${already.version} is already selected but another dependency needs ` + requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ")
19466
+ );
19467
+ }
19468
+ continue;
19469
+ }
19470
+ options.progress.collecting(requirement.name);
19471
+ const resolved = await resolvePackage(options.client, requirement);
19472
+ chosen.set(requirement.name, resolved);
19473
+ options.progress.downloading(resolved.name, resolved.version);
19474
+ const archive = await options.client.bytes(resolved.url);
19475
+ const entries = readZip(archive);
19476
+ const metadata = entries.find((entry) => /\.dist-info\/METADATA$/.test(entry.name));
19477
+ if (metadata) {
19478
+ for (const line of new TextDecoder().decode(metadata.data()).split(/\r?\n/)) {
19479
+ if (!line.startsWith("Requires-Dist:")) continue;
19480
+ const dependency = parseRequirement(line.slice("Requires-Dist:".length));
19481
+ if (dependency) queue.push({ requirement: dependency, extras: [] });
19482
+ }
19483
+ }
19484
+ unpack(options.vfs, options.cred, entries);
19485
+ report.installed.push({ name: resolved.name, version: resolved.version });
19486
+ }
19487
+ return report;
19488
+ }
19489
+ function unpack(vfs, cred, entries, resolved) {
19490
+ const staged = [];
19491
+ for (const entry of entries) {
19492
+ if (entry.isDirectory) continue;
19493
+ if (/^[^/]+\.data\//.test(entry.name)) continue;
19494
+ staged.push({ path: `${SITE_PACKAGES}/${entry.name}`, data: entry.data() });
19495
+ }
19496
+ const directories = /* @__PURE__ */ new Set();
19497
+ for (const file3 of staged) directories.add(file3.path.slice(0, file3.path.lastIndexOf("/")));
19498
+ for (const directory2 of directories) vfs.mkdir(directory2, { recursive: true, cred });
19499
+ for (const file3 of staged) vfs.writeFile(file3.path, file3.data, { cred });
19500
+ }
19501
+
19502
+ // src/runtime/python/backend.ts
19503
+ function withSitePackages(env2) {
19504
+ const existing = env2.PYTHONPATH;
19505
+ return {
19506
+ ...env2,
19507
+ PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES
19508
+ };
19509
+ }
19510
+ var INTERPRETER_OWNED = /* @__PURE__ */ new Set(["lib", "dev", "proc", "usr"]);
19511
+ async function resolveWorkerUrl(explicit) {
19512
+ if (explicit) {
19513
+ try {
19514
+ return new URL(explicit);
19515
+ } catch {
19516
+ return explicit;
19517
+ }
19518
+ }
19519
+ const beside = new URL("./python-worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
19520
+ if (beside.protocol !== "file:") return beside;
19521
+ const [{ existsSync }, { fileURLToPath: fileURLToPath2, pathToFileURL: pathToFileURL2 }] = await Promise.all([
19522
+ nodeBuiltin("fs"),
19523
+ nodeBuiltin("url")
19524
+ ]);
19525
+ if (existsSync(fileURLToPath2(beside))) return beside;
19526
+ const built = new URL("../../../dist/python-worker.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
19527
+ if (existsSync(fileURLToPath2(built))) return pathToFileURL2(fileURLToPath2(built));
19528
+ throw new Error(
19529
+ "the SandboxedJs Python process worker has not been built; run `npm run build` in the package, or pass configurePython({ workerUrl })."
19530
+ );
19531
+ }
19532
+ function usingOwnedPython() {
19533
+ return pythonBackend().backend === "sbx-cpython-wasm";
19534
+ }
19535
+ async function runOwnedPython(ctx, argv) {
19536
+ const { manifest, workerUrl } = pythonBackend();
19537
+ if (!manifest) throw new Error("no Python runtime manifest is configured");
19538
+ const mounts = containerMounts(ctx);
19539
+ const process2 = await startPythonProcess({
19540
+ manifest,
19541
+ workerUrl: await resolveWorkerUrl(workerUrl),
19542
+ vfs: ctx.vfs,
19543
+ cred: ctx.cred,
19544
+ pid: ctx.proc.pid,
19545
+ ppid: ctx.proc.ppid ?? 1,
19546
+ argv,
19547
+ env: withSitePackages(ctx.env),
19548
+ cwd: ctx.cwd,
19549
+ isTty: isTerminal(ctx),
19550
+ mounts,
19551
+ onStdout: (data) => ctx.stdout.write(data),
19552
+ onStderr: (data) => ctx.stderr.write(data),
19553
+ signal: ctx.signal
19554
+ });
19555
+ void pumpStdin(ctx, process2);
19556
+ return process2.wait();
19557
+ }
19558
+ async function pumpStdin(ctx, proc) {
19559
+ const stdin = ctx.stdin;
19560
+ try {
19561
+ for (; ; ) {
19562
+ const chunk = await stdin.read(65536);
19563
+ if (chunk === null || chunk.length === 0) break;
19564
+ proc.write(chunk);
19565
+ }
19566
+ } catch {
19567
+ }
19568
+ proc.endInput();
19569
+ }
19570
+ function isTerminal(ctx) {
19571
+ const stdin = ctx.stdin;
19572
+ return Boolean(stdin.isTTY || stdin.interactive);
19573
+ }
19574
+ function containerMounts(ctx) {
19575
+ const mounts = [];
19576
+ let entries;
19577
+ try {
19578
+ entries = ctx.vfs.readdir("/", ctx.cred);
19579
+ } catch {
19580
+ return mounts;
19581
+ }
19582
+ for (const name of entries) {
19583
+ if (INTERPRETER_OWNED.has(name)) continue;
19584
+ try {
19585
+ if (!ctx.vfs.stat(`/${name}`, { cred: ctx.cred }).isDirectory()) continue;
19586
+ } catch {
19587
+ continue;
19588
+ }
19589
+ mounts.push({ guest: `/${name}`, container: `/${name}` });
19590
+ }
19591
+ try {
19592
+ if (ctx.vfs.stat(SITE_PACKAGES, { cred: ctx.cred }).isDirectory()) {
19593
+ mounts.push({ guest: SITE_PACKAGES, container: SITE_PACKAGES });
19594
+ }
19595
+ } catch {
19596
+ }
19597
+ return mounts;
19598
+ }
19599
+
19600
+ // src/runtime/python/pip-command.ts
19601
+ var pipCommand = defineCommand({
19602
+ name: "pip",
19603
+ path: "/usr/bin/pip",
19604
+ aliases: ["pip3"],
19605
+ summary: "install Python wheels into the container's site-packages",
19606
+ usage: "pip install [-r requirements.txt] <package>...",
19607
+ manual: `Resolves wheels from PyPI and unpacks them into ${SITE_PACKAGES}, which
19608
+ the interpreter has on sys.path.
19609
+
19610
+ This is not pip. It installs published wheels only: there are no source builds,
19611
+ no editable installs, no uninstall, and one site-packages rather than virtual
19612
+ environments. A package with no usable wheel is an error, never a silent skip.`,
19613
+ async run(ctx) {
19614
+ const [action, ...args] = ctx.args;
19615
+ if (action === "--version" || action === "-V") {
19616
+ ctx.line(`pip (sandboxedjs wheel installer, not pip) targeting ${SITE_PACKAGES}`);
19617
+ return 0;
19618
+ }
19619
+ if (action === "list") {
19620
+ return listInstalled(ctx);
19621
+ }
19622
+ if (action !== "install") {
19623
+ ctx.line("usage: pip install [-r requirements.txt] <package>...");
19624
+ return action === void 0 ? 1 : 0;
19625
+ }
19626
+ const requirements = [];
19627
+ for (let i = 0; i < args.length; i += 1) {
19628
+ const arg = args[i];
19629
+ if (arg === "-r" || arg === "--requirement") {
19630
+ const file3 = args[++i];
19631
+ if (!file3) return ctx.fail("option -r requires a file", 2);
19632
+ try {
19633
+ requirements.push(
19634
+ ...ctx.vfs.readText(ctx.path(file3), ctx.cred).split(/\r?\n/).map((line) => line.replace(/#.*$/, "").trim()).filter(Boolean)
19635
+ );
19636
+ } catch {
19637
+ return ctx.fail(`could not open requirements file '${file3}'`);
19638
+ }
19639
+ continue;
19640
+ }
19641
+ if (arg.startsWith("-")) continue;
19642
+ requirements.push(arg);
19643
+ }
19644
+ if (requirements.length === 0) return ctx.fail("no packages specified");
19645
+ if (!ctx.kernel.net.outboundAllowed("https://pypi.org")) {
19646
+ return ctx.fail(
19647
+ "outbound network access is disabled for this container (enable with network: { allowOutbound: true })"
19648
+ );
19649
+ }
19650
+ const client = {
19651
+ async json(url) {
19652
+ const response = await fetch(url);
19653
+ if (!response.ok) throw new Error(`${url} answered ${response.status}`);
19654
+ return response.json();
19655
+ },
19656
+ async bytes(url) {
19657
+ const response = await fetch(url);
19658
+ if (!response.ok) throw new Error(`${url} answered ${response.status}`);
19659
+ return new Uint8Array(await response.arrayBuffer());
19660
+ }
19661
+ };
19662
+ try {
19663
+ const report = await installRequirements({
19664
+ client,
19665
+ vfs: ctx.vfs,
19666
+ cred: ctx.cred,
19667
+ requirements,
19668
+ pythonVersion: pythonBackend().manifest?.pythonVersion ?? "3.13.5",
19669
+ progress: {
19670
+ collecting: (name) => ctx.line(`Collecting ${name}`),
19671
+ downloading: () => {
19672
+ }
19673
+ }
19674
+ });
19675
+ for (const skipped of report.skipped) {
19676
+ ctx.line(` Skipping ${skipped.name} (marker: ${skipped.marker})`);
19677
+ }
19678
+ ctx.line(
19679
+ `Successfully installed ${report.installed.map((p) => `${p.name}-${p.version}`).join(" ")}`
19680
+ );
19681
+ return 0;
19682
+ } catch (error) {
19683
+ return ctx.fail(error.message ?? String(error));
19684
+ }
19685
+ }
19686
+ });
19687
+ function listInstalled(ctx) {
19688
+ let entries;
19689
+ try {
19690
+ entries = ctx.vfs.readdir(SITE_PACKAGES, ctx.cred);
19691
+ } catch {
19692
+ return 0;
19693
+ }
19694
+ for (const entry of entries) {
19695
+ const match2 = /^(.+)-(.+)\.dist-info$/.exec(entry);
19696
+ if (match2) ctx.line(`${match2[1]} ${match2[2]}`);
19697
+ }
19698
+ return 0;
19699
+ }
19700
+
19701
+ // src/runtime/emscripten-fs.ts
19702
+ init_errno();
19703
+ init_path();
19704
+ var EM_ERRNO = {
19705
+ EPERM: 63,
19706
+ ENOENT: 44,
19707
+ EIO: 29,
19708
+ EBADF: 8,
19709
+ EACCES: 2,
19710
+ EBUSY: 10,
19711
+ EEXIST: 20,
19712
+ EXDEV: 75,
19713
+ ENODEV: 43,
19714
+ ENOTDIR: 54,
19715
+ EISDIR: 31,
19716
+ EINVAL: 28,
19717
+ ENFILE: 41,
19718
+ EMFILE: 33,
19719
+ ENOSPC: 51,
19720
+ EROFS: 57,
19721
+ ENOTEMPTY: 55,
19722
+ ELOOP: 32,
19723
+ ENOSYS: 52,
19724
+ ENOTSUP: 138
19725
+ };
19726
+ function emErrno(code) {
19727
+ return EM_ERRNO[code] ?? EM_ERRNO.EIO;
19728
+ }
19729
+ function mountContainerFs(FS, opts) {
19730
+ const { vfs, cred } = opts;
19731
+ const throwErrno = (code) => {
19732
+ throw new FS.ErrnoError(emErrno(code));
19733
+ };
19734
+ const translate = (e) => {
19735
+ if (isSysError(e)) return throwErrno(e.code);
19736
+ throw new FS.ErrnoError(emErrno("EIO"));
19737
+ };
19738
+ const mountRoot = opts.mountAt ? normalize(opts.mountAt) : "/";
19739
+ const backend = {
19740
+ /** Absolute container path for a node. */
19741
+ realPath(node2) {
19742
+ const parts = [];
19743
+ let current = node2;
19744
+ while (current && current.parent !== current) {
19745
+ parts.unshift(current.name);
19746
+ current = current.parent;
19747
+ }
19748
+ const relative2 = parts.join("/");
19749
+ if (mountRoot === "/") return "/" + relative2;
19750
+ return relative2 ? join(mountRoot, relative2) : mountRoot;
19751
+ },
19752
+ mount(_mount) {
19753
+ return backend.createNode(null, "/", 16877 | 0, 0);
19754
+ },
19755
+ createNode(parent, name, mode, rdev) {
19756
+ const node2 = FS.createNode(parent, name, mode, rdev);
19757
+ node2.node_ops = nodeOps;
19758
+ node2.stream_ops = streamOps;
19759
+ return node2;
19760
+ }
19761
+ };
19762
+ const nodeOps = {
19763
+ getattr(node2) {
19764
+ const path = backend.realPath(node2);
19765
+ try {
19766
+ const st = vfs.lstat(path);
19767
+ return {
19768
+ dev: st.dev,
19769
+ ino: st.ino,
19770
+ mode: st.mode,
19771
+ nlink: st.nlink,
19772
+ uid: st.uid,
19773
+ gid: st.gid,
19774
+ rdev: st.rdev,
19775
+ size: st.size,
19776
+ atime: new Date(st.atimeMs),
19777
+ mtime: new Date(st.mtimeMs),
19778
+ ctime: new Date(st.ctimeMs),
19779
+ blksize: 4096,
19780
+ blocks: Math.ceil(st.size / 512)
19781
+ };
19782
+ } catch (e) {
19783
+ return translate(e);
19784
+ }
19785
+ },
19786
+ setattr(node2, attr) {
19787
+ const path = backend.realPath(node2);
19788
+ try {
19789
+ if (attr.mode !== void 0) {
19790
+ vfs.chmod(path, attr.mode & 4095, cred);
19791
+ node2.mode = attr.mode;
19792
+ }
19793
+ if (attr.timestamp !== void 0) vfs.utimes(path, attr.timestamp, attr.timestamp, cred);
19794
+ if (attr.size !== void 0) {
19795
+ vfs.truncate(path, attr.size, cred);
19796
+ opts.onWrite?.(path);
19797
+ }
19798
+ } catch (e) {
19799
+ translate(e);
19800
+ }
19801
+ },
19802
+ lookup(parent, name) {
19803
+ const path = join(backend.realPath(parent), name);
19804
+ let mode;
19805
+ try {
19806
+ mode = vfs.lstat(path).mode;
19807
+ } catch {
19808
+ throw new FS.ErrnoError(EM_ERRNO.ENOENT);
19809
+ }
19810
+ return backend.createNode(parent, name, mode, 0);
19811
+ },
19812
+ mknod(parent, name, mode, dev) {
19813
+ const path = join(backend.realPath(parent), name);
19814
+ try {
19815
+ if (FS.isDir(mode)) {
19816
+ vfs.mkdir(path, { cred, mode: mode & 4095 });
19817
+ } else if (FS.isFile(mode)) {
19818
+ vfs.writeFile(path, new Uint8Array(0), { cred, mode: mode & 4095 });
19819
+ } else {
19820
+ return throwErrno("ENOTSUP");
19821
+ }
19822
+ opts.onWrite?.(path);
19823
+ } catch (e) {
19824
+ translate(e);
19825
+ }
19826
+ return backend.createNode(parent, name, mode, dev);
19827
+ },
19828
+ rename(oldNode, newDir, newName) {
19829
+ const from = backend.realPath(oldNode);
19830
+ const to = join(backend.realPath(newDir), newName);
19831
+ try {
19832
+ vfs.rename(from, to, cred);
19833
+ opts.onWrite?.(to);
19834
+ } catch (e) {
19835
+ translate(e);
19836
+ }
19837
+ oldNode.name = newName;
19838
+ oldNode.parent = newDir;
19839
+ },
19840
+ unlink(parent, name) {
19841
+ const path = join(backend.realPath(parent), name);
19842
+ try {
19843
+ vfs.unlink(path, cred);
19844
+ opts.onWrite?.(path);
17832
19845
  } catch (e) {
17833
19846
  translate(e);
17834
19847
  }
@@ -18715,9 +20728,9 @@ async function resolveIndexUrl() {
18715
20728
  if (indexUrl) return indexUrl;
18716
20729
  if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
18717
20730
  try {
18718
- const { createRequire: createRequire2 } = await nodeBuiltin("module");
20731
+ const { createRequire } = await nodeBuiltin("module");
18719
20732
  const path = await nodeBuiltin("path");
18720
- const require2 = createRequire2(path.join(process.cwd(), "index.js"));
20733
+ const require2 = createRequire(path.join(process.cwd(), "index.js"));
18721
20734
  return `${path.dirname(require2.resolve("pyodide/package.json"))}/`;
18722
20735
  } catch {
18723
20736
  return void 0;
@@ -18997,6 +21010,12 @@ var micropip = defineCommand({
18997
21010
  summary: "install Python packages into the running interpreter",
18998
21011
  usage: "micropip install <package>...",
18999
21012
  async run(ctx) {
21013
+ if (usingOwnedPython()) {
21014
+ ctx.warn(
21015
+ "pip here is micropip, which installs into the Pyodide interpreter \u2014 not the source-built CPython this container is running, so the packages would not be importable.\nRun the container with the pyodide backend if you need micropip, or wait for venv and pip support on the owned runtime (see docs/python/release-gates.md, M4)."
21016
+ );
21017
+ return 1;
21018
+ }
19000
21019
  const [action, ...args] = ctx.args;
19001
21020
  if (action === "--version" || action === "-V") {
19002
21021
  ctx.line("pip (micropip, Pyodide)");
@@ -19083,6 +21102,11 @@ var micropip = defineCommand({
19083
21102
  var PYTHON_VERSION = "3.13";
19084
21103
  function configurePython(options = {}) {
19085
21104
  configureCPython({ indexURL: options.indexURL, moduleURL: options.pyodideURL });
21105
+ setPythonBackend({
21106
+ ...options.backend !== void 0 ? { backend: options.backend } : {},
21107
+ ...options.manifest !== void 0 ? { manifest: options.manifest } : {},
21108
+ ...options.workerUrl !== void 0 ? { workerUrl: options.workerUrl } : {}
21109
+ });
19086
21110
  }
19087
21111
  var isPythonAvailable = isCPythonAvailable;
19088
21112
  var python = defineCommand({
@@ -19095,6 +21119,13 @@ var python = defineCommand({
19095
21119
  the container's virtual filesystem, modules in /workspace are importable, and
19096
21120
  compatible packages can be installed with pip (micropip).`,
19097
21121
  async run(ctx) {
21122
+ if (usingOwnedPython()) {
21123
+ try {
21124
+ return await runOwnedPython(ctx, ctx.args);
21125
+ } catch (error) {
21126
+ return ctx.fail(error.message ?? String(error));
21127
+ }
21128
+ }
19098
21129
  const argv = ctx.args;
19099
21130
  let i = 0;
19100
21131
  let command;
@@ -19175,22 +21206,22 @@ function printHelp2(ctx) {
19175
21206
  ctx.line("-V : print the Python version and exit");
19176
21207
  }
19177
21208
  function pythonCommands() {
19178
- return [python, micropip];
21209
+ return usingOwnedPython() ? [python, pipCommand] : [python, micropip];
19179
21210
  }
19180
21211
 
19181
21212
  // src/runtime/ffmpeg.ts
19182
21213
  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";
19183
21214
  var compiled = null;
19184
21215
  async function loadFactory() {
19185
- const { createRequire: createRequire2 } = await nodeBuiltin("module");
21216
+ const { createRequire } = await nodeBuiltin("module");
19186
21217
  const fs = await nodeBuiltin("fs/promises");
19187
21218
  const path = await nodeBuiltin("path");
19188
- const packageRequire = createRequire2((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
21219
+ const packageRequire = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
19189
21220
  let require2 = packageRequire;
19190
21221
  try {
19191
21222
  require2.resolve("@ffmpeg/core/wasm");
19192
21223
  } catch {
19193
- require2 = createRequire2(path.join(process.cwd(), "index.js"));
21224
+ require2 = createRequire(path.join(process.cwd(), "index.js"));
19194
21225
  }
19195
21226
  const wasmPath = require2.resolve("@ffmpeg/core/wasm");
19196
21227
  const wasm = await fs.readFile(wasmPath);
@@ -20582,7 +22613,9 @@ function safeTarget(destination, name) {
20582
22613
  return join(destination, clean2);
20583
22614
  }
20584
22615
  function stripPackageRoot(name) {
20585
- return name.replace(/^\.\//, "").replace(/^package\//, "").replace(/\/$/, "");
22616
+ const clean2 = name.replace(/^\.\//, "").replace(/\/$/, "");
22617
+ const slash = clean2.indexOf("/");
22618
+ return slash === -1 ? "" : clean2.slice(slash + 1);
20586
22619
  }
20587
22620
  function text(bytes2, start2, length) {
20588
22621
  return new TextDecoder().decode(bytes2.subarray(start2, start2 + length)).replace(/\0.*$/, "").trim();
@@ -21717,19 +23750,19 @@ var KernelChildProcess = class {
21717
23750
  process;
21718
23751
  started = false;
21719
23752
  cancelled = false;
21720
- constructor(kernel, cred, config, pid) {
23753
+ constructor(kernel, cred, config2, pid) {
21721
23754
  this.stdin.interactive = true;
21722
- if (config.inheritStdio) this.stdin.isTTY = true;
21723
- if (config.stdinIgnored) this.stdin.end();
23755
+ if (config2.inheritStdio) this.stdin.isTTY = true;
23756
+ if (config2.stdinIgnored) this.stdin.end();
21724
23757
  this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
21725
23758
  this.kernel = kernel;
21726
23759
  this.cred = cred;
21727
23760
  this.pid = pid;
21728
- this.command = config.command;
21729
- this.args = config.args?.slice() ?? [];
21730
- this.parentPid = config.parentPid;
21731
- this.cwd = config.cwd ?? "/";
21732
- this.env = { ...config.env };
23761
+ this.command = config2.command;
23762
+ this.args = config2.args?.slice() ?? [];
23763
+ this.parentPid = config2.parentPid;
23764
+ this.cwd = config2.cwd ?? "/";
23765
+ this.env = { ...config2.env };
21733
23766
  }
21734
23767
  on(event, listener) {
21735
23768
  let listeners = this.listeners.get(event);
@@ -21836,12 +23869,12 @@ var nextBridgePid = 1073741824;
21836
23869
  function installNodeChildProcessBridge(pod, kernel, cred) {
21837
23870
  const manager = pod.processManager;
21838
23871
  const originalSpawn = manager.spawn;
21839
- manager.spawn = (config) => {
21840
- const resolved = kernel.resolveExecutable(config.command, config.cwd ?? "/", config.env ?? {}, cred);
23872
+ manager.spawn = (config2) => {
23873
+ const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
21841
23874
  if (resolved?.kind === "builtin") {
21842
- return new KernelChildProcess(kernel, cred, config, nextBridgePid++);
23875
+ return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
21843
23876
  }
21844
- return originalSpawn.call(manager, config);
23877
+ return originalSpawn.call(manager, config2);
21845
23878
  };
21846
23879
  return () => {
21847
23880
  if (manager.spawn === originalSpawn) return;
@@ -23203,14 +25236,14 @@ var AssertionError = class extends Error {
23203
25236
  code = "ERR_ASSERTION";
23204
25237
  constructor(options) {
23205
25238
  const generated = options.message === void 0;
23206
- super(options.message ?? describe(options.actual, options.expected, options.operator));
25239
+ super(options.message ?? describe2(options.actual, options.expected, options.operator));
23207
25240
  this.actual = options.actual;
23208
25241
  this.expected = options.expected;
23209
25242
  this.operator = options.operator;
23210
25243
  this.generatedMessage = generated;
23211
25244
  }
23212
25245
  };
23213
- function describe(actual, expected, operator) {
25246
+ function describe2(actual, expected, operator) {
23214
25247
  if (operator === "fail") return "Failed";
23215
25248
  const rendered = (value) => inspect(value, { depth: 2 });
23216
25249
  return `${rendered(actual)} ${operator} ${rendered(expected)}`;
@@ -24048,9 +26081,9 @@ var VirtualClientRequest = class extends streamModule4__default.default.Writable
24048
26081
  } catch (error) {
24049
26082
  clearTimeout(this.timer);
24050
26083
  if (this.destroyedByUser) return;
24051
- const failure = error instanceof Error ? error : new Error(String(error));
24052
- if (!("code" in failure)) Object.assign(failure, { code: "ECONNREFUSED" });
24053
- this.emit("error", failure);
26084
+ const failure2 = error instanceof Error ? error : new Error(String(error));
26085
+ if (!("code" in failure2)) Object.assign(failure2, { code: "ECONNREFUSED" });
26086
+ this.emit("error", failure2);
24054
26087
  } finally {
24055
26088
  if (!settled) untrack();
24056
26089
  }
@@ -24248,17 +26281,17 @@ function toBytes2(data, encoding) {
24248
26281
  }
24249
26282
 
24250
26283
  // src/runtime/sync-channel.ts
24251
- var STATE = 0;
24252
- var LENGTH = 1;
24253
- var MORE = 2;
24254
- var CONTROL_WORDS = 4;
24255
- var STATE_REQUEST = 1;
24256
- var STATE_RESPONSE = 2;
24257
- var STATE_CONTINUE = 3;
24258
- var STATE_CLOSED = 4;
26284
+ var STATE2 = 0;
26285
+ var LENGTH2 = 1;
26286
+ var MORE2 = 2;
26287
+ var CONTROL_WORDS2 = 4;
26288
+ var STATE_REQUEST2 = 1;
26289
+ var STATE_RESPONSE2 = 2;
26290
+ var STATE_CONTINUE2 = 3;
26291
+ var STATE_CLOSED2 = 4;
24259
26292
  function createSyncChannelBuffers(capacityBytes = 1 << 20) {
24260
26293
  return {
24261
- control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
26294
+ control: new SharedArrayBuffer(CONTROL_WORDS2 * Int32Array.BYTES_PER_ELEMENT),
24262
26295
  data: new SharedArrayBuffer(capacityBytes)
24263
26296
  };
24264
26297
  }
@@ -24292,15 +26325,15 @@ var SyncChannelServer = class {
24292
26325
  /** Call from the wake message the client sends after each chunk. */
24293
26326
  async pump() {
24294
26327
  if (this.closed) return;
24295
- const state = Atomics.load(this.control, STATE);
24296
- if (state === STATE_REQUEST) {
24297
- const size = Atomics.load(this.control, LENGTH);
26328
+ const state = Atomics.load(this.control, STATE2);
26329
+ if (state === STATE_REQUEST2) {
26330
+ const size = Atomics.load(this.control, LENGTH2);
24298
26331
  this.incoming.push(this.data.slice(0, size));
24299
- if (Atomics.load(this.control, MORE) === 1) {
24300
- this.publish(STATE_CONTINUE);
26332
+ if (Atomics.load(this.control, MORE2) === 1) {
26333
+ this.publish(STATE_CONTINUE2);
24301
26334
  return;
24302
26335
  }
24303
- const request = concat3(this.incoming);
26336
+ const request = concat4(this.incoming);
24304
26337
  this.incoming = [];
24305
26338
  let response;
24306
26339
  try {
@@ -24314,30 +26347,30 @@ var SyncChannelServer = class {
24314
26347
  this.sendChunk();
24315
26348
  return;
24316
26349
  }
24317
- if (state === STATE_CONTINUE) this.sendChunk();
26350
+ if (state === STATE_CONTINUE2) this.sendChunk();
24318
26351
  }
24319
26352
  /** Release a blocked client, e.g. when the container is torn down. */
24320
26353
  close() {
24321
26354
  if (this.closed) return;
24322
26355
  this.closed = true;
24323
- Atomics.store(this.control, STATE, STATE_CLOSED);
24324
- Atomics.notify(this.control, STATE);
26356
+ Atomics.store(this.control, STATE2, STATE_CLOSED2);
26357
+ Atomics.notify(this.control, STATE2);
24325
26358
  }
24326
26359
  sendChunk() {
24327
26360
  const payload = this.outgoing ?? new Uint8Array();
24328
26361
  const size = Math.min(this.capacity, payload.length - this.sent);
24329
26362
  this.data.set(payload.subarray(this.sent, this.sent + size), 0);
24330
26363
  this.sent += size;
24331
- Atomics.store(this.control, LENGTH, size);
24332
- Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
24333
- this.publish(STATE_RESPONSE);
26364
+ Atomics.store(this.control, LENGTH2, size);
26365
+ Atomics.store(this.control, MORE2, this.sent < payload.length ? 1 : 0);
26366
+ this.publish(STATE_RESPONSE2);
24334
26367
  }
24335
26368
  publish(state) {
24336
- Atomics.store(this.control, STATE, state);
24337
- Atomics.notify(this.control, STATE);
26369
+ Atomics.store(this.control, STATE2, state);
26370
+ Atomics.notify(this.control, STATE2);
24338
26371
  }
24339
26372
  };
24340
- function concat3(parts) {
26373
+ function concat4(parts) {
24341
26374
  if (parts.length === 1) return parts[0];
24342
26375
  const total = parts.reduce((sum, part) => sum + part.length, 0);
24343
26376
  const joined = new Uint8Array(total);
@@ -24470,13 +26503,13 @@ ${err.join("")}`),
24470
26503
  return child;
24471
26504
  };
24472
26505
  const exec = (command, options = {}, callback) => {
24473
- const [opts, cb] = normalize2(options, callback);
26506
+ const [opts, cb] = normalize3(options, callback);
24474
26507
  const { file: file3, args } = throughShell(command, opts);
24475
26508
  return collect(start2(file3, args, { ...opts, shell: false }), opts, cb);
24476
26509
  };
24477
26510
  const execFile = (file3, args = [], options = {}, callback) => {
24478
26511
  const list = Array.isArray(args) ? args : [];
24479
- const [opts, cb] = Array.isArray(args) ? normalize2(options, callback) : normalize2(args, options);
26512
+ const [opts, cb] = Array.isArray(args) ? normalize3(options, callback) : normalize3(args, options);
24480
26513
  return collect(start2(file3, list, opts), opts, cb);
24481
26514
  };
24482
26515
  return {
@@ -24548,7 +26581,7 @@ ${result.stderr}`), {
24548
26581
  const execSync = (command, options = {}) => orThrow(run(command, [], { ...options, shell: options.shell ?? true }), command, options);
24549
26582
  return { spawnSync, execFileSync, execSync };
24550
26583
  }
24551
- function normalize2(options, callback) {
26584
+ function normalize3(options, callback) {
24552
26585
  if (typeof options === "function") return [{}, options];
24553
26586
  return [options ?? {}, callback];
24554
26587
  }
@@ -24940,7 +26973,7 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
24940
26973
  }
24941
26974
  }
24942
26975
  }
24943
- const isTerminal = (options, output) => {
26976
+ const isTerminal2 = (options, output) => {
24944
26977
  if (typeof options?.terminal === "boolean") return options.terminal;
24945
26978
  return Boolean(output?.isTTY);
24946
26979
  };
@@ -24950,13 +26983,13 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
24950
26983
  const instance = new Interface(
24951
26984
  options.input ?? defaultInput(),
24952
26985
  resolvedOutput2,
24953
- isTerminal(options, resolvedOutput2)
26986
+ isTerminal2(options, resolvedOutput2)
24954
26987
  );
24955
26988
  if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
24956
26989
  return instance;
24957
26990
  }
24958
26991
  const resolvedOutput = output;
24959
- return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal(void 0, resolvedOutput));
26992
+ return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal2(void 0, resolvedOutput));
24960
26993
  };
24961
26994
  const noop = () => {
24962
26995
  };
@@ -26628,15 +28661,91 @@ var MirroringVolume = class {
26628
28661
  mirror.writeFileSync(path, this.inner.readFileSync(path));
26629
28662
  });
26630
28663
  }
26631
- // ── reads: straight through ───────────────────────────────────────────────
28664
+ // ── reads: straight through, then the mirror ──────────────────────────────
28665
+ /*
28666
+ * A file the bundler produced exists only in the mirror until something
28667
+ * asks for it here.
28668
+ *
28669
+ * {@link absorb} was the original answer and is not enough on its own. It
28670
+ * runs when a process ends — a dev server never ends — and it skips
28671
+ * `node_modules`, where Vite keeps the one output that a dev server depends
28672
+ * on: `node_modules/.vite/deps`. Between those two, the entire pre-bundled
28673
+ * dependency set stayed invisible to the container that had to serve it.
28674
+ *
28675
+ * The symptom was almost perfectly disguised. Vite writes `_metadata.json`
28676
+ * itself, through this volume, so the directory looked half-populated and
28677
+ * the metadata listed every dependency as optimized. Rolldown wrote the
28678
+ * actual bundles through WASI into the mirror, where nothing brought them
28679
+ * back. Vite then read a file that was not there — and reports a failed read
28680
+ * of an optimized dependency as `504 Outdated Optimize Dep`, which describes
28681
+ * a stale hash and sends everyone looking at hashes. The hash was never
28682
+ * wrong. The file was missing.
28683
+ *
28684
+ * Falling back on read fixes it wherever it happens, for any path, without
28685
+ * polling and without depending on how the engine chose to write.
28686
+ */
26632
28687
  readFileSync(path) {
26633
- return this.inner.readFileSync(path);
28688
+ try {
28689
+ return this.inner.readFileSync(path);
28690
+ } catch (error) {
28691
+ const produced = this.fromMirror(path);
28692
+ if (!produced) throw error;
28693
+ try {
28694
+ this.ensureDirectory(dirname(path));
28695
+ this.inner.writeFileSync(path, produced);
28696
+ } catch {
28697
+ }
28698
+ return produced;
28699
+ }
26634
28700
  }
26635
28701
  readdirSync(path) {
26636
- return this.inner.readdirSync(path);
28702
+ let here = [];
28703
+ let missing;
28704
+ try {
28705
+ here = this.inner.readdirSync(path);
28706
+ } catch (error) {
28707
+ missing = error;
28708
+ }
28709
+ const mirrored = this.mirrorNames(path);
28710
+ if (!mirrored) {
28711
+ if (missing) throw missing;
28712
+ return here;
28713
+ }
28714
+ return [.../* @__PURE__ */ new Set([...here, ...mirrored])].sort();
26637
28715
  }
26638
28716
  lstatSync(path) {
26639
- return this.inner.lstatSync(path);
28717
+ try {
28718
+ return this.inner.lstatSync(path);
28719
+ } catch (error) {
28720
+ if (this.fromMirror(path)) {
28721
+ try {
28722
+ this.readFileSync(path);
28723
+ return this.inner.lstatSync(path);
28724
+ } catch {
28725
+ }
28726
+ }
28727
+ throw error;
28728
+ }
28729
+ }
28730
+ /** The mirror's copy of a file, if it has one and we are allowed to look. */
28731
+ fromMirror(path) {
28732
+ const mirror = this.mirror;
28733
+ if (!mirror?.readFileSync || !mirror.lstatSync || !this.mirrored(path)) return void 0;
28734
+ try {
28735
+ if (!mirror.lstatSync(path).isDirectory()) return mirror.readFileSync(path);
28736
+ } catch {
28737
+ }
28738
+ return void 0;
28739
+ }
28740
+ /** The mirror's entries for a directory, for merging into a listing. */
28741
+ mirrorNames(path) {
28742
+ const mirror = this.mirror;
28743
+ if (!mirror?.readdirSync || !this.mirrored(path)) return void 0;
28744
+ try {
28745
+ return mirror.readdirSync(path);
28746
+ } catch {
28747
+ return void 0;
28748
+ }
26640
28749
  }
26641
28750
  readlinkSync(path) {
26642
28751
  return this.inner.readlinkSync(path);
@@ -26665,11 +28774,76 @@ var MirroringVolume = class {
26665
28774
  this.inner.unlinkSync(path);
26666
28775
  this.sync(path);
26667
28776
  }
28777
+ /**
28778
+ * Rename, carrying anything the mirrored engine put there.
28779
+ *
28780
+ * This is the operation the dependency optimizer is built on, and the one
28781
+ * that used to destroy its output. Vite bundles into a temporary directory
28782
+ * and renames it into place when it is complete — but only half of that
28783
+ * directory is ever on this volume. Vite writes `_metadata.json` and
28784
+ * `package.json` through here; Rolldown writes the actual bundles through
28785
+ * WASI into the mirror. Renaming moved the half that was here and then
28786
+ * `sync(from)` deleted the half that was not, because from this side the
28787
+ * source directory had simply gone.
28788
+ *
28789
+ * What survived was a `deps` directory holding metadata that described five
28790
+ * optimized dependencies and contained none of them — which Vite reports, on
28791
+ * the failed read, as `504 Outdated Optimize Dep`. Nothing in that message is
28792
+ * true, and following it leads to hashes rather than to a missing file.
28793
+ *
28794
+ * So the mirror's side of the move is collected first, and lands here as
28795
+ * part of the rename.
28796
+ */
26668
28797
  renameSync(from, to) {
26669
28798
  this.inner.renameSync(from, to);
28799
+ this.collect(from, to);
26670
28800
  this.sync(from);
26671
28801
  this.sync(to);
26672
28802
  }
28803
+ /**
28804
+ * Copy whatever the mirror holds under `from` into this volume under `to`.
28805
+ *
28806
+ * Deliberately not filtered the way {@link absorb} is: this walks one
28807
+ * directory that is being renamed, not the whole project, so the subtree is
28808
+ * small and `node_modules` is exactly where the interesting case lives.
28809
+ */
28810
+ collect(from, to) {
28811
+ const mirror = this.mirror;
28812
+ if (!mirror?.readdirSync || !mirror.readFileSync || !mirror.lstatSync) return;
28813
+ if (!this.mirrored(from)) return;
28814
+ const walk = (source, destination) => {
28815
+ let stat2;
28816
+ try {
28817
+ stat2 = mirror.lstatSync(source);
28818
+ } catch {
28819
+ return;
28820
+ }
28821
+ if (stat2.isSymbolicLink()) return;
28822
+ if (stat2.isDirectory()) {
28823
+ let names;
28824
+ try {
28825
+ names = mirror.readdirSync(source);
28826
+ } catch {
28827
+ return;
28828
+ }
28829
+ this.ensureDirectory(destination);
28830
+ for (const name of names) walk(join(source, name), join(destination, name));
28831
+ return;
28832
+ }
28833
+ try {
28834
+ this.inner.lstatSync(destination);
28835
+ return;
28836
+ } catch {
28837
+ }
28838
+ try {
28839
+ const bytes2 = mirror.readFileSync(source);
28840
+ this.ensureDirectory(dirname(destination));
28841
+ this.inner.writeFileSync(destination, bytes2);
28842
+ } catch {
28843
+ }
28844
+ };
28845
+ this.safely(() => walk(from, to));
28846
+ }
26673
28847
  symlinkSync(target, path) {
26674
28848
  this.inner.symlinkSync(target, path);
26675
28849
  this.sync(path);
@@ -26782,44 +28956,69 @@ function ensureProcessGlobal() {
26782
28956
  configurable: true
26783
28957
  });
26784
28958
  }
28959
+
28960
+ // src/runtime/rolldown-node-binding.ts
28961
+ async function loadNodeApi() {
28962
+ const [moduleApi, fsApi, pathApi, urlApi] = await Promise.all([
28963
+ nodeBuiltin("module"),
28964
+ nodeBuiltin("fs"),
28965
+ nodeBuiltin("path"),
28966
+ nodeBuiltin("url")
28967
+ ]);
28968
+ return {
28969
+ createRequire: moduleApi.createRequire,
28970
+ existsSync: fsApi.existsSync,
28971
+ readFileSync: fsApi.readFileSync,
28972
+ dirname: pathApi.dirname,
28973
+ join: pathApi.join,
28974
+ fileURLToPath: urlApi.fileURLToPath,
28975
+ pathToFileURL: urlApi.pathToFileURL
28976
+ };
28977
+ }
26785
28978
  var BINDING_PACKAGE = "@rolldown/binding-wasm32-wasi";
26786
28979
  var ASYNC_WORK_POOL_SIZE = 4;
26787
28980
  var WORKER_FILE = "rolldown-wasi-worker.js";
26788
28981
  async function loadNodeRolldownMemfsBinding() {
26789
- const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
28982
+ const node2 = await loadNodeApi();
28983
+ const { createRequire, existsSync, readFileSync, dirname: dirname3, join: join3, fileURLToPath: fileURLToPath2, pathToFileURL: pathToFileURL2 } = node2;
28984
+ const require2 = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
26790
28985
  let packageDir;
26791
28986
  try {
26792
- packageDir = path.dirname(require2.resolve(`${BINDING_PACKAGE}/package.json`));
28987
+ packageDir = dirname3(require2.resolve(`${BINDING_PACKAGE}/package.json`));
26793
28988
  } catch {
26794
28989
  return null;
26795
28990
  }
26796
- const fromBinding = module$1.createRequire(path.join(packageDir, "package.json"));
28991
+ const fromBinding = createRequire(join3(packageDir, "package.json"));
26797
28992
  let runtime;
26798
28993
  let fsModuleUrl;
26799
28994
  let createContext2;
26800
28995
  try {
26801
28996
  runtime = fromBinding("@napi-rs/wasm-runtime");
26802
- fsModuleUrl = resolveSubpath(fromBinding, "@napi-rs/wasm-runtime", "./fs");
28997
+ fsModuleUrl = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", "./fs");
26803
28998
  ({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
26804
28999
  } catch {
26805
29000
  return null;
26806
29001
  }
26807
- const { memfs } = await import(fsModuleUrl);
26808
- const { Worker: Worker2 } = await import('worker_threads');
29002
+ const { memfs } = await import(
29003
+ /* @vite-ignore */
29004
+ /* webpackIgnore: true */
29005
+ fsModuleUrl
29006
+ );
29007
+ const { Worker: Worker2 } = await nodeBuiltin("worker_threads");
26809
29008
  const { fs: memoryFs, vol } = memfs();
26810
29009
  const wasi2 = new runtime.WASI({
26811
29010
  version: "preview1",
26812
29011
  fs: memoryFs,
26813
29012
  preopens: { "/": "/" }
26814
29013
  });
26815
- const wasmFile = fs.readFileSync(path.join(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
29014
+ const wasmFile = readFileSync(join3(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
26816
29015
  const sharedMemory = new WebAssembly.Memory({ initial: 16384, maximum: 65536, shared: true });
26817
29016
  const workerPoolSize = Math.max(2, cpuCount());
26818
29017
  const context = createContext2({ autoDestroy: false });
26819
29018
  context.suppressDestroy();
26820
- const workerPath = resolveWorkerPath();
29019
+ const workerPath = resolveWorkerPath(node2);
26821
29020
  if (!workerPath) return null;
26822
- const runtimeAnchor = url.pathToFileURL(path.join(packageDir, "package.json")).href;
29021
+ const runtimeAnchor = pathToFileURL2(join3(packageDir, "package.json")).href;
26823
29022
  const workers = /* @__PURE__ */ new Set();
26824
29023
  let started = false;
26825
29024
  const { napiModule } = await runtime.instantiateNapiModule(wasmFile, {
@@ -26871,24 +29070,26 @@ async function loadNodeRolldownMemfsBinding() {
26871
29070
  for (const worker of workers) unreferenceWorker(worker);
26872
29071
  return { ...napiModule.exports, __fs: memoryFs, __volume: vol };
26873
29072
  }
26874
- function resolveWorkerPath() {
26875
- const here = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
29073
+ function resolveWorkerPath(node2) {
29074
+ const { dirname: dirname3, existsSync, fileURLToPath: fileURLToPath2, join: join3 } = node2;
29075
+ const here = dirname3(fileURLToPath2((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
26876
29076
  const candidates = [
26877
- path.join(here, WORKER_FILE),
29077
+ join3(here, WORKER_FILE),
26878
29078
  /* src/runtime → dist, for a checkout that has been built. */
26879
- path.join(here, "..", "..", "dist", WORKER_FILE)
29079
+ join3(here, "..", "..", "dist", WORKER_FILE)
26880
29080
  ];
26881
- return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
29081
+ return candidates.find((candidate) => existsSync(candidate)) ?? null;
26882
29082
  }
26883
- function resolveSubpath(require2, packageName, subpath) {
26884
- const packageDir = path.dirname(require2.resolve(packageName));
26885
- const manifest = JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf8"));
29083
+ function resolveSubpath(node2, require2, packageName, subpath) {
29084
+ const { dirname: dirname3, join: join3, pathToFileURL: pathToFileURL2, readFileSync } = node2;
29085
+ const packageDir = dirname3(require2.resolve(packageName));
29086
+ const manifest = JSON.parse(readFileSync(join3(packageDir, "package.json"), "utf8"));
26886
29087
  const entry = manifest?.exports?.[subpath];
26887
29088
  const target = typeof entry === "string" ? entry : entry?.import ?? entry?.default;
26888
29089
  if (typeof target !== "string") {
26889
29090
  throw new Error(`${packageName} does not export ${subpath}`);
26890
29091
  }
26891
- return url.pathToFileURL(path.join(packageDir, target)).href;
29092
+ return pathToFileURL2(join3(packageDir, target)).href;
26892
29093
  }
26893
29094
  function cpuCount() {
26894
29095
  try {
@@ -27067,9 +29268,9 @@ var LocalProcess = class extends EventEmitter4__default.default {
27067
29268
  }
27068
29269
  };
27069
29270
  var PodChildProcess = class {
27070
- constructor(pid, config, launch) {
29271
+ constructor(pid, config2, launch) {
27071
29272
  this.pid = pid;
27072
- this.config = config;
29273
+ this.config = config2;
27073
29274
  this.launch = launch;
27074
29275
  }
27075
29276
  pid;
@@ -27207,15 +29408,54 @@ var LocalRuntimePod = class _LocalRuntimePod {
27207
29408
  instanceId = `sbx-${Math.random().toString(36).slice(2)}`;
27208
29409
  router = new VirtualHttpRouter();
27209
29410
  proxy = { activePorts: (_instanceId) => this.router.activePorts() };
29411
+ /**
29412
+ * Bind a port to a server that is not a JavaScript one.
29413
+ *
29414
+ * The request is flattened to bytes and headers before it crosses the
29415
+ * boundary, because the far side is a different language and a Node stream
29416
+ * object cannot go there. What comes back is a complete response; streaming
29417
+ * is a later protocol, and pretending to stream over a buffered path would
29418
+ * only move the surprise.
29419
+ */
29420
+ serveExternal(port, owner, handler) {
29421
+ const server = new VirtualHttpServer(this.router, owner);
29422
+ server.on("request", (request, response) => {
29423
+ void (async () => {
29424
+ const chunks = [];
29425
+ for await (const chunk of request) chunks.push(chunk);
29426
+ const body = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
29427
+ let at = 0;
29428
+ for (const chunk of chunks) {
29429
+ body.set(chunk, at);
29430
+ at += chunk.length;
29431
+ }
29432
+ try {
29433
+ const answer = await handler({
29434
+ method: request.method,
29435
+ path: request.url,
29436
+ headers: request.headers,
29437
+ body
29438
+ });
29439
+ response.writeHead(answer.statusCode, answer.headers ?? {});
29440
+ response.end(answer.body ?? "");
29441
+ } catch (error) {
29442
+ response.writeHead(500, { "content-type": "text/plain" });
29443
+ response.end(error instanceof Error ? error.message : String(error));
29444
+ }
29445
+ })();
29446
+ });
29447
+ server.listen(port);
29448
+ return () => server.close();
29449
+ }
27210
29450
  /**
27211
29451
  * Mutable on purpose: a container replaces `spawn` so that children resolve
27212
29452
  * against the kernel's PATH. Left alone, it runs `node` and reports anything
27213
29453
  * else as not found, which is the correct answer for a bare pod.
27214
29454
  */
27215
29455
  processManager = {
27216
- spawn: (config) => new PodChildProcess(
29456
+ spawn: (config2) => new PodChildProcess(
27217
29457
  nextChildPid++,
27218
- config,
29458
+ config2,
27219
29459
  (child) => this.spawn(child.command, child.args ?? [], {
27220
29460
  ...child.cwd ? { cwd: child.cwd } : {},
27221
29461
  ...child.env ? { env: child.env } : {}
@@ -27288,7 +29528,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
27288
29528
  if (engine?.isEvaluating) throw new ProcessExit(code);
27289
29529
  },
27290
29530
  http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
27291
- spawnChild: (config) => this.processManager.spawn(config),
29531
+ spawnChild: (config2) => this.processManager.spawn(config2),
27292
29532
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
27293
29533
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
27294
29534
  ...options.tty ? { tty: true } : {},
@@ -27509,10 +29749,10 @@ function serveVolume(volume) {
27509
29749
  }
27510
29750
  return encodeFrame({ ok: true, value: result });
27511
29751
  } catch (error) {
27512
- const failure = error;
29752
+ const failure2 = error;
27513
29753
  return encodeFrame({
27514
29754
  ok: false,
27515
- error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
29755
+ error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
27516
29756
  });
27517
29757
  }
27518
29758
  };
@@ -27529,7 +29769,7 @@ function serveSyncSyscalls(options) {
27529
29769
  const result = await options.spawnChild(header.args[0]);
27530
29770
  return encodeFrame({ ok: true, value: result });
27531
29771
  } catch (error) {
27532
- const failure = error;
29772
+ const failure2 = error;
27533
29773
  return encodeFrame({
27534
29774
  ok: true,
27535
29775
  value: {
@@ -27537,81 +29777,10 @@ function serveSyncSyscalls(options) {
27537
29777
  stdout: "",
27538
29778
  stderr: "",
27539
29779
  signal: null,
27540
- error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
27541
- }
27542
- });
27543
- }
27544
- };
27545
- }
27546
-
27547
- // src/runtime/worker-host.ts
27548
- function defaultWorkerUrl() {
27549
- return new URL("./worker-entry.js", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
27550
- }
27551
- function hasDomWorker() {
27552
- return typeof Worker === "function";
27553
- }
27554
- async function startRuntimeWorker(options = {}) {
27555
- const url = options.url ?? defaultWorkerUrl();
27556
- const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
27557
- try {
27558
- await new Promise((resolve2, reject) => {
27559
- const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
27560
- worker.onMessage((message) => {
27561
- if (message?.type === "sandboxedjs:ready") {
27562
- clearTimeout(timer);
27563
- resolve2();
29780
+ error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
27564
29781
  }
27565
29782
  });
27566
- worker.onError((error) => {
27567
- clearTimeout(timer);
27568
- reject(error instanceof Error ? error : new Error(String(error)));
27569
- });
27570
- });
27571
- } catch (error) {
27572
- try {
27573
- await worker.terminate();
27574
- } catch {
27575
29783
  }
27576
- throw error;
27577
- }
27578
- return worker;
27579
- }
27580
- async function startDomWorker(url) {
27581
- const worker = new Worker(url, { type: "module" });
27582
- const listeners = [];
27583
- const errors = [];
27584
- worker.addEventListener("message", (event) => {
27585
- for (const listener of listeners) listener(event.data);
27586
- });
27587
- worker.addEventListener("error", (event) => {
27588
- const detail = event.message || "the runtime worker failed to load";
27589
- for (const listener of errors) listener(new Error(detail));
27590
- });
27591
- return {
27592
- postMessage: (message) => worker.postMessage(message),
27593
- onMessage: (listener) => {
27594
- listeners.push(listener);
27595
- },
27596
- onError: (listener) => {
27597
- errors.push(listener);
27598
- },
27599
- terminate: () => worker.terminate()
27600
- };
27601
- }
27602
- async function startNodeWorker(url, workerData) {
27603
- const specifier = ["node", "worker_threads"].join(":");
27604
- const { Worker: NodeWorker } = await import(
27605
- /* @vite-ignore */
27606
- /* webpackIgnore: true */
27607
- specifier
27608
- );
27609
- const worker = new NodeWorker(url, { workerData });
27610
- return {
27611
- postMessage: (message) => worker.postMessage(message),
27612
- onMessage: (listener) => worker.on("message", listener),
27613
- onError: (listener) => worker.on("error", listener),
27614
- terminate: () => worker.terminate()
27615
29784
  };
27616
29785
  }
27617
29786
  var WorkerProcess = class extends EventEmitter4__default.default {
@@ -27875,8 +30044,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
27875
30044
  ...request.inheritStdio ? { inheritStdio: true } : {}
27876
30045
  });
27877
30046
  } catch (error) {
27878
- const failure = error;
27879
- resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure.code ? { code: failure.code } : {}, message: failure.message } });
30047
+ const failure2 = error;
30048
+ resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message } });
27880
30049
  return;
27881
30050
  }
27882
30051
  owned.add(handle);
@@ -27931,7 +30100,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
27931
30100
  async forward(worker, port, request, response) {
27932
30101
  const chunks = [];
27933
30102
  for await (const chunk of request) chunks.push(chunk);
27934
- const body = concat4(chunks);
30103
+ const body = concat5(chunks);
27935
30104
  const id = this.nextRequestId++;
27936
30105
  const answered = new Promise((resolve2) => this.waiting.set(id, resolve2));
27937
30106
  worker.postMessage({
@@ -28012,7 +30181,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
28012
30181
  function isRecord2(value) {
28013
30182
  return typeof value === "object" && value !== null && !Array.isArray(value);
28014
30183
  }
28015
- function concat4(parts) {
30184
+ function concat5(parts) {
28016
30185
  if (parts.length === 0) return new Uint8Array();
28017
30186
  if (parts.length === 1) return parts[0];
28018
30187
  const total = parts.reduce((sum, part) => sum + part.length, 0);
@@ -29163,6 +31332,8 @@ exports.KERNEL_NAME = KERNEL_NAME;
29163
31332
  exports.KERNEL_RELEASE = KERNEL_RELEASE;
29164
31333
  exports.Kernel = Kernel;
29165
31334
  exports.LocalRuntimePod = LocalRuntimePod;
31335
+ exports.MANIFEST_FORMAT = MANIFEST_FORMAT;
31336
+ exports.MANIFEST_SCHEMA_VERSION = MANIFEST_SCHEMA_VERSION;
29166
31337
  exports.MemoryVolume = MemoryVolume;
29167
31338
  exports.NODE_VERSION = NODE_VERSION;
29168
31339
  exports.NPM_VERSION = NPM_VERSION;
@@ -29244,6 +31415,7 @@ exports.strerror = strerror;
29244
31415
  exports.syncChannelSupported = syncChannelSupported;
29245
31416
  exports.transformEsm = transformEsm;
29246
31417
  exports.unameInfo = unameInfo;
31418
+ exports.validateManifest = validateManifest;
29247
31419
  exports.wasiCommand = wasi;
29248
31420
  //# sourceMappingURL=index.cjs.map
29249
31421
  //# sourceMappingURL=index.cjs.map