sandboxedjs 0.1.48 → 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.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Buffer as Buffer$1 } from 'buffer/index.js';
2
1
  import { ungzip, gzip as gzip$1, deflate as deflate$1, inflate as inflate$1, deflateRaw, inflateRaw } from 'pako';
2
+ import { Buffer as Buffer$1 } from 'buffer/index.js';
3
3
  import { sha256, sha224 } from '@noble/hashes/sha256';
4
4
  import { sha512, sha384 } from '@noble/hashes/sha512';
5
5
  import { sha1 } from '@noble/hashes/sha1';
@@ -17,10 +17,6 @@ import timersModule from 'timers-browserify';
17
17
  import legacyUrl from 'url/url.js';
18
18
  import { sha1 as sha1$1, md5 } from '@noble/hashes/legacy';
19
19
  import { hmac } from '@noble/hashes/hmac';
20
- import { createRequire } from 'module';
21
- import { readFileSync, existsSync } from 'fs';
22
- import { dirname as dirname$1, join as join$1 } from 'path';
23
- import { pathToFileURL as pathToFileURL$1, fileURLToPath as fileURLToPath$1 } from 'url';
24
20
 
25
21
  var __defProp = Object.defineProperty;
26
22
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -3594,10 +3590,10 @@ function builtinType({ shell, argv, io }) {
3594
3590
  }
3595
3591
  async function builtinCommand({ shell, argv, io }) {
3596
3592
  const args = argv.slice(1);
3597
- const describe2 = args.includes("-v") || args.includes("-V");
3593
+ const describe3 = args.includes("-v") || args.includes("-V");
3598
3594
  const verbose = args.includes("-V");
3599
3595
  const rest = args.filter((a) => a !== "-v" && a !== "-V" && a !== "-p");
3600
- if (describe2) {
3596
+ if (describe3) {
3601
3597
  let status = 0;
3602
3598
  for (const name of rest) {
3603
3599
  if (isBuiltinName(name)) {
@@ -13910,15 +13906,15 @@ var sort = defineCommand({
13910
13906
  const slice = fields.slice(start2, end === void 0 ? void 0 : end);
13911
13907
  return slice.join(separator ?? " ");
13912
13908
  };
13913
- const normalize3 = (line) => {
13909
+ const normalize4 = (line) => {
13914
13910
  let value = keys.length ? fieldOf(line, keys[0]) : line;
13915
13911
  if (args.has("ignore-leading-blanks")) value = value.replace(/^\s+/, "");
13916
13912
  if (args.has("ignore-case")) value = value.toLowerCase();
13917
13913
  return value;
13918
13914
  };
13919
13915
  const compare = (a, b) => {
13920
- const x = normalize3(a);
13921
- const y = normalize3(b);
13916
+ const x = normalize4(a);
13917
+ const y = normalize4(b);
13922
13918
  if (args.has("numeric-sort") || args.has("general-numeric-sort")) {
13923
13919
  const nx = parseFloat(x) || 0;
13924
13920
  const ny = parseFloat(y) || 0;
@@ -15159,7 +15155,7 @@ var diff = defineCommand({
15159
15155
  if (a === void 0 || b === void 0) return ctx.fail("missing operand", 2);
15160
15156
  const { sources, ok: ok2 } = await readInputs(ctx, [a, b]);
15161
15157
  if (!ok2 || sources.length < 2) return 2;
15162
- const normalize3 = (line) => {
15158
+ const normalize4 = (line) => {
15163
15159
  let value = line;
15164
15160
  if (args.has("ignore-all-space")) value = value.replace(/\s+/g, "");
15165
15161
  else if (args.has("ignore-space-change")) value = value.replace(/\s+/g, " ").trim();
@@ -15168,7 +15164,7 @@ var diff = defineCommand({
15168
15164
  };
15169
15165
  const left = splitLines(sources[0].bytes).lines;
15170
15166
  const right = splitLines(sources[1].bytes).lines;
15171
- const script = diffLines(left.map(normalize3), right.map(normalize3));
15167
+ const script = diffLines(left.map(normalize4), right.map(normalize4));
15172
15168
  if (script.length === 0) return 0;
15173
15169
  if (args.has("brief")) {
15174
15170
  ctx.line(`Files ${a} and ${b} differ`);
@@ -17668,150 +17664,2167 @@ function nodeCommands() {
17668
17664
  // src/runtime/python.ts
17669
17665
  init_path();
17670
17666
 
17671
- // src/runtime/emscripten-fs.ts
17672
- init_errno();
17673
- init_path();
17674
- var EM_ERRNO = {
17675
- EPERM: 63,
17676
- ENOENT: 44,
17677
- EIO: 29,
17678
- EBADF: 8,
17679
- EACCES: 2,
17680
- EBUSY: 10,
17681
- EEXIST: 20,
17682
- EXDEV: 75,
17683
- ENODEV: 43,
17684
- ENOTDIR: 54,
17685
- EISDIR: 31,
17686
- EINVAL: 28,
17687
- ENFILE: 41,
17688
- EMFILE: 33,
17689
- ENOSPC: 51,
17690
- EROFS: 57,
17691
- ENOTEMPTY: 55,
17692
- ELOOP: 32,
17693
- ENOSYS: 52,
17694
- ENOTSUP: 138
17667
+ // src/runtime/python/host-abi.ts
17668
+ var SBX_HOST_ABI_VERSION = 1;
17669
+ var SBX_REQUEST_HEADER_BYTES = 16;
17670
+ var SBX_RESPONSE_HEADER_BYTES = 20;
17671
+ var Op = {
17672
+ handshake: 0,
17673
+ openat: 1,
17674
+ close: 2,
17675
+ read: 3,
17676
+ write: 4,
17677
+ pread: 5,
17678
+ pwrite: 6,
17679
+ lseek: 7,
17680
+ fstat: 8,
17681
+ statat: 9,
17682
+ ftruncate: 10,
17683
+ renameat: 11,
17684
+ unlinkat: 12,
17685
+ mkdirat: 13,
17686
+ readlinkat: 14,
17687
+ symlinkat: 15,
17688
+ getdents: 16,
17689
+ fsync: 17,
17690
+ dup: 256,
17691
+ dup2: 257,
17692
+ get_flags: 258,
17693
+ set_flags: 259,
17694
+ pipe: 512,
17695
+ poll: 768,
17696
+ clock_gettime: 1024,
17697
+ sleep: 1025,
17698
+ getpid: 1280,
17699
+ getcwd: 1281,
17700
+ chdir: 1282,
17701
+ environ: 1283,
17702
+ getrandom: 1536
17695
17703
  };
17696
- function emErrno(code) {
17697
- return EM_ERRNO[code] ?? EM_ERRNO.EIO;
17704
+ var Errno = {
17705
+ EPERM: 1,
17706
+ ENOENT: 2,
17707
+ ESRCH: 3,
17708
+ EINTR: 4,
17709
+ EIO: 5,
17710
+ ENXIO: 6,
17711
+ E2BIG: 7,
17712
+ EBADF: 9,
17713
+ EAGAIN: 11,
17714
+ ENOMEM: 12,
17715
+ EACCES: 13,
17716
+ EFAULT: 14,
17717
+ EBUSY: 16,
17718
+ EEXIST: 17,
17719
+ EXDEV: 18,
17720
+ ENODEV: 19,
17721
+ ENOTDIR: 20,
17722
+ EISDIR: 21,
17723
+ EINVAL: 22,
17724
+ ENFILE: 23,
17725
+ EMFILE: 24,
17726
+ ENOTTY: 25,
17727
+ EFBIG: 27,
17728
+ ENOSPC: 28,
17729
+ ESPIPE: 29,
17730
+ EROFS: 30,
17731
+ EMLINK: 31,
17732
+ EPIPE: 32,
17733
+ ERANGE: 34,
17734
+ ENAMETOOLONG: 36,
17735
+ ENOSYS: 38,
17736
+ ENOTEMPTY: 39,
17737
+ ELOOP: 40,
17738
+ ENODATA: 61,
17739
+ EPROTO: 71,
17740
+ EOVERFLOW: 75,
17741
+ ETIMEDOUT: 110,
17742
+ ECANCELED: 125,
17743
+ EDQUOT: 122
17744
+ };
17745
+ var CAPABILITIES = ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy", "sockets", "processes", "signals", "storage", "services", "threads"];
17746
+ var OP_NAMES = Object.fromEntries(
17747
+ Object.entries(Op).map(([name, code]) => [code, name])
17748
+ );
17749
+ Object.fromEntries(
17750
+ Object.entries(Errno).map(([name, code]) => [code, name])
17751
+ );
17752
+
17753
+ // src/runtime/python/manifest.ts
17754
+ var MANIFEST_FORMAT = "sandboxedjs-python-runtime";
17755
+ var MANIFEST_SCHEMA_VERSION = 1;
17756
+ var ManifestError = class extends Error {
17757
+ code = "ERR_SBX_PYTHON_MANIFEST";
17758
+ };
17759
+ function validateManifest(value) {
17760
+ const manifest = value;
17761
+ if (!manifest || typeof manifest !== "object") throw new ManifestError("the Python runtime manifest is not an object");
17762
+ if (manifest.format !== MANIFEST_FORMAT) {
17763
+ throw new ManifestError(`not a ${MANIFEST_FORMAT} manifest (format: ${String(manifest.format)})`);
17764
+ }
17765
+ if (manifest.schemaVersion !== MANIFEST_SCHEMA_VERSION) {
17766
+ throw new ManifestError(
17767
+ `manifest schema version ${String(manifest.schemaVersion)}, this build understands ${MANIFEST_SCHEMA_VERSION}`
17768
+ );
17769
+ }
17770
+ if (manifest.engine !== "cpython-wasm") {
17771
+ throw new ManifestError(`unknown Python engine ${String(manifest.engine)}`);
17772
+ }
17773
+ if (!manifest.runtimeId) throw new ManifestError("the manifest has no runtimeId");
17774
+ if (!manifest.artifacts?.moduleUrl) throw new ManifestError("the manifest names no module artifact");
17775
+ const abi = manifest.hostAbi;
17776
+ if (!abi || abi.name !== "sbx_host_v1") {
17777
+ throw new ManifestError(`the runtime was built against host ABI ${String(abi?.name)}, not sbx_host_v1`);
17778
+ }
17779
+ if (abi.version !== SBX_HOST_ABI_VERSION) {
17780
+ throw new ManifestError(
17781
+ `the runtime was built against host ABI version ${abi.version}; this kernel speaks ${SBX_HOST_ABI_VERSION}`
17782
+ );
17783
+ }
17784
+ if (!manifest.capabilities) throw new ManifestError("the manifest declares no capabilities");
17785
+ return manifest;
17698
17786
  }
17699
- function mountContainerFs(FS, opts) {
17700
- const { vfs, cred } = opts;
17701
- const throwErrno = (code) => {
17702
- throw new FS.ErrnoError(emErrno(code));
17703
- };
17704
- const translate = (e) => {
17705
- if (isSysError(e)) return throwErrno(e.code);
17706
- throw new FS.ErrnoError(emErrno("EIO"));
17707
- };
17708
- const mountRoot = opts.mountAt ? normalize(opts.mountAt) : "/";
17709
- const backend = {
17710
- /** Absolute container path for a node. */
17711
- realPath(node2) {
17712
- const parts = [];
17713
- let current = node2;
17714
- while (current && current.parent !== current) {
17715
- parts.unshift(current.name);
17716
- current = current.parent;
17717
- }
17718
- const relative2 = parts.join("/");
17719
- if (mountRoot === "/") return "/" + relative2;
17720
- return relative2 ? join(mountRoot, relative2) : mountRoot;
17721
- },
17722
- mount(_mount) {
17723
- return backend.createNode(null, "/", 16877 | 0, 0);
17724
- },
17725
- createNode(parent, name, mode, rdev) {
17726
- const node2 = FS.createNode(parent, name, mode, rdev);
17727
- node2.node_ops = nodeOps;
17728
- node2.stream_ops = streamOps;
17729
- return node2;
17787
+
17788
+ // src/runtime/python/config.ts
17789
+ var bundledManifest = {
17790
+ format: "sandboxedjs-python-runtime",
17791
+ schemaVersion: 1,
17792
+ runtimeId: "sbx-cpython-3.13.5-threaded-fixed",
17793
+ engine: "cpython-wasm",
17794
+ pythonVersion: "3.13.5",
17795
+ profile: "threaded-fixed",
17796
+ hostAbi: { name: "sbx_host_v1", version: 1 },
17797
+ artifacts: {
17798
+ moduleUrl: new URL(
17799
+ import.meta.url.includes("/src/runtime/python/") ? "../../../dist/python/python.js" : "./python/python.js",
17800
+ import.meta.url
17801
+ ).href
17802
+ },
17803
+ capabilities: {
17804
+ threads: true,
17805
+ nativeExtensions: "fixed",
17806
+ networking: "none",
17807
+ processes: "none",
17808
+ persistence: "memory"
17809
+ }
17810
+ };
17811
+ var config = { backend: "sbx-cpython-wasm", manifest: bundledManifest };
17812
+ function setPythonBackend(options) {
17813
+ if (options.manifest !== void 0) config.manifest = validateManifest(options.manifest);
17814
+ if (options.workerUrl !== void 0) config.workerUrl = options.workerUrl;
17815
+ if (options.backend !== void 0) {
17816
+ if (options.backend === "sbx-cpython-wasm" && !config.manifest) {
17817
+ throw new Error(
17818
+ "the sbx-cpython-wasm backend needs a runtime manifest; pass one to configurePython({ manifest })"
17819
+ );
17730
17820
  }
17731
- };
17732
- const nodeOps = {
17733
- getattr(node2) {
17734
- const path = backend.realPath(node2);
17735
- try {
17736
- const st = vfs.lstat(path);
17737
- return {
17738
- dev: st.dev,
17739
- ino: st.ino,
17740
- mode: st.mode,
17741
- nlink: st.nlink,
17742
- uid: st.uid,
17743
- gid: st.gid,
17744
- rdev: st.rdev,
17745
- size: st.size,
17746
- atime: new Date(st.atimeMs),
17747
- mtime: new Date(st.mtimeMs),
17748
- ctime: new Date(st.ctimeMs),
17749
- blksize: 4096,
17750
- blocks: Math.ceil(st.size / 512)
17751
- };
17752
- } catch (e) {
17753
- return translate(e);
17754
- }
17755
- },
17756
- setattr(node2, attr) {
17757
- const path = backend.realPath(node2);
17758
- try {
17759
- if (attr.mode !== void 0) {
17760
- vfs.chmod(path, attr.mode & 4095, cred);
17761
- node2.mode = attr.mode;
17762
- }
17763
- if (attr.timestamp !== void 0) vfs.utimes(path, attr.timestamp, attr.timestamp, cred);
17764
- if (attr.size !== void 0) {
17765
- vfs.truncate(path, attr.size, cred);
17766
- opts.onWrite?.(path);
17767
- }
17768
- } catch (e) {
17769
- translate(e);
17770
- }
17771
- },
17772
- lookup(parent, name) {
17773
- const path = join(backend.realPath(parent), name);
17774
- let mode;
17775
- try {
17776
- mode = vfs.lstat(path).mode;
17777
- } catch {
17778
- throw new FS.ErrnoError(EM_ERRNO.ENOENT);
17779
- }
17780
- return backend.createNode(parent, name, mode, 0);
17781
- },
17782
- mknod(parent, name, mode, dev) {
17783
- const path = join(backend.realPath(parent), name);
17784
- try {
17785
- if (FS.isDir(mode)) {
17786
- vfs.mkdir(path, { cred, mode: mode & 4095 });
17787
- } else if (FS.isFile(mode)) {
17788
- vfs.writeFile(path, new Uint8Array(0), { cred, mode: mode & 4095 });
17789
- } else {
17790
- return throwErrno("ENOTSUP");
17791
- }
17792
- opts.onWrite?.(path);
17793
- } catch (e) {
17794
- translate(e);
17795
- }
17796
- return backend.createNode(parent, name, mode, dev);
17797
- },
17798
- rename(oldNode, newDir, newName) {
17799
- const from = backend.realPath(oldNode);
17800
- const to = join(backend.realPath(newDir), newName);
17821
+ config.backend = options.backend;
17822
+ }
17823
+ }
17824
+ function pythonBackend() {
17825
+ return config;
17826
+ }
17827
+
17828
+ // src/kernel/open-file.ts
17829
+ var PosixError = class extends Error {
17830
+ constructor(errno, message) {
17831
+ super(message ?? `errno ${errno}`);
17832
+ this.errno = errno;
17833
+ this.name = "PosixError";
17834
+ }
17835
+ errno;
17836
+ };
17837
+ var O_RDONLY = 0;
17838
+ var O_WRONLY = 1;
17839
+ var O_CREAT2 = 64;
17840
+ var O_EXCL2 = 128;
17841
+ var O_TRUNC2 = 512;
17842
+ var O_APPEND = 1024;
17843
+ var O_NONBLOCK = 2048;
17844
+ var O_DIRECTORY2 = 65536;
17845
+ var O_CLOEXEC = 524288;
17846
+ function accessMode(flags) {
17847
+ return flags & 3;
17848
+ }
17849
+ var SEEK_SET = 0;
17850
+ var SEEK_CUR = 1;
17851
+ var Inode = class {
17852
+ constructor(ino, path, vfs, cred) {
17853
+ this.ino = ino;
17854
+ this.path = path;
17855
+ this.vfs = vfs;
17856
+ this.cred = cred;
17857
+ }
17858
+ ino;
17859
+ path;
17860
+ vfs;
17861
+ cred;
17862
+ detached = null;
17863
+ refs = 0;
17864
+ /** Read the whole file, wherever it currently lives. */
17865
+ contents() {
17866
+ if (this.detached) return this.detached;
17867
+ try {
17868
+ return this.vfs.readFile(this.path, this.cred);
17869
+ } catch (error) {
17870
+ throw toPosixError(error);
17871
+ }
17872
+ }
17873
+ replace(data) {
17874
+ if (this.detached) {
17875
+ this.detached = data;
17876
+ return;
17877
+ }
17878
+ try {
17879
+ this.vfs.writeFile(this.path, data, { cred: this.cred });
17880
+ } catch (error) {
17881
+ throw toPosixError(error);
17882
+ }
17883
+ }
17884
+ /** Called when the last name is about to be removed. */
17885
+ detach() {
17886
+ if (this.detached) return;
17887
+ try {
17888
+ this.detached = this.vfs.readFile(this.path, this.cred);
17889
+ } catch {
17890
+ this.detached = new Uint8Array();
17891
+ }
17892
+ }
17893
+ size() {
17894
+ return this.contents().length;
17895
+ }
17896
+ stat() {
17897
+ if (this.detached) {
17898
+ return {
17899
+ ino: this.ino,
17900
+ mode: 33188,
17901
+ size: this.detached.length,
17902
+ uid: this.cred.uid,
17903
+ gid: this.cred.gid,
17904
+ nlink: 0,
17905
+ atimeMs: Date.now(),
17906
+ mtimeMs: Date.now(),
17907
+ ctimeMs: Date.now()
17908
+ };
17909
+ }
17910
+ try {
17911
+ const st = this.vfs.stat(this.path, { cred: this.cred });
17912
+ return {
17913
+ ino: st.ino,
17914
+ mode: st.mode,
17915
+ size: st.size,
17916
+ uid: st.uid,
17917
+ gid: st.gid,
17918
+ nlink: st.nlink ?? 1,
17919
+ atimeMs: st.atimeMs,
17920
+ mtimeMs: st.mtimeMs,
17921
+ ctimeMs: st.ctimeMs
17922
+ };
17923
+ } catch (error) {
17924
+ throw toPosixError(error);
17925
+ }
17926
+ }
17927
+ };
17928
+ var FileDescription = class {
17929
+ constructor(inode, flags) {
17930
+ this.inode = inode;
17931
+ this.flags = flags;
17932
+ inode.refs += 1;
17933
+ }
17934
+ inode;
17935
+ flags;
17936
+ kind = "file";
17937
+ offset = 0;
17938
+ read(length) {
17939
+ if (accessMode(this.flags) === O_WRONLY) throw new PosixError(Errno.EBADF);
17940
+ const data = this.inode.contents();
17941
+ const start2 = Math.min(this.offset, data.length);
17942
+ const end = Math.min(start2 + length, data.length);
17943
+ this.offset = end;
17944
+ return data.slice(start2, end);
17945
+ }
17946
+ pread(length, offset) {
17947
+ if (accessMode(this.flags) === O_WRONLY) throw new PosixError(Errno.EBADF);
17948
+ const data = this.inode.contents();
17949
+ const start2 = Math.min(offset, data.length);
17950
+ return data.slice(start2, Math.min(start2 + length, data.length));
17951
+ }
17952
+ write(data) {
17953
+ if (accessMode(this.flags) === O_RDONLY) throw new PosixError(Errno.EBADF);
17954
+ const at = this.flags & O_APPEND ? this.inode.size() : this.offset;
17955
+ const written = this.writeAt(data, at);
17956
+ this.offset = at + written;
17957
+ return written;
17958
+ }
17959
+ pwrite(data, offset) {
17960
+ if (accessMode(this.flags) === O_RDONLY) throw new PosixError(Errno.EBADF);
17961
+ return this.writeAt(data, offset);
17962
+ }
17963
+ writeAt(data, offset) {
17964
+ const current = this.inode.contents();
17965
+ const end = offset + data.length;
17966
+ const next = new Uint8Array(Math.max(current.length, end));
17967
+ next.set(current, 0);
17968
+ next.set(data, offset);
17969
+ this.inode.replace(next);
17970
+ return data.length;
17971
+ }
17972
+ seek(offset, whence) {
17973
+ const base2 = whence === SEEK_SET ? 0 : whence === SEEK_CUR ? this.offset : this.inode.size();
17974
+ const next = base2 + offset;
17975
+ if (next < 0) throw new PosixError(Errno.EINVAL);
17976
+ this.offset = next;
17977
+ return next;
17978
+ }
17979
+ truncate(length) {
17980
+ const current = this.inode.contents();
17981
+ const next = new Uint8Array(length);
17982
+ next.set(current.subarray(0, Math.min(length, current.length)), 0);
17983
+ this.inode.replace(next);
17984
+ }
17985
+ stat() {
17986
+ return this.inode.stat();
17987
+ }
17988
+ readable() {
17989
+ return true;
17990
+ }
17991
+ writable() {
17992
+ return true;
17993
+ }
17994
+ whenReady() {
17995
+ return Promise.resolve();
17996
+ }
17997
+ close() {
17998
+ this.inode.refs -= 1;
17999
+ if (this.inode.refs <= 0) this.inode.detached = null;
18000
+ }
18001
+ };
18002
+ var DirectoryDescription = class {
18003
+ constructor(path, flags, vfs, cred) {
18004
+ this.path = path;
18005
+ this.flags = flags;
18006
+ this.vfs = vfs;
18007
+ this.cred = cred;
18008
+ }
18009
+ path;
18010
+ flags;
18011
+ vfs;
18012
+ cred;
18013
+ kind = "dir";
18014
+ entries = null;
18015
+ index = 0;
18016
+ /** Entry names, NUL-terminated, as many as fit in `length`. */
18017
+ read(length) {
18018
+ if (!this.entries) {
17801
18019
  try {
17802
- vfs.rename(from, to, cred);
17803
- opts.onWrite?.(to);
17804
- } catch (e) {
17805
- translate(e);
18020
+ this.entries = [".", "..", ...this.vfs.readdir(this.path, this.cred)];
18021
+ } catch (error) {
18022
+ throw toPosixError(error);
17806
18023
  }
17807
- oldNode.name = newName;
17808
- oldNode.parent = newDir;
17809
- },
17810
- unlink(parent, name) {
17811
- const path = join(backend.realPath(parent), name);
17812
- try {
17813
- vfs.unlink(path, cred);
17814
- opts.onWrite?.(path);
18024
+ }
18025
+ const encoder9 = new TextEncoder();
18026
+ const parts = [];
18027
+ let total = 0;
18028
+ while (this.index < this.entries.length) {
18029
+ const encoded = encoder9.encode(this.entries[this.index] + "\0");
18030
+ if (total + encoded.length > length) break;
18031
+ parts.push(encoded);
18032
+ total += encoded.length;
18033
+ this.index += 1;
18034
+ }
18035
+ const joined = new Uint8Array(total);
18036
+ let at = 0;
18037
+ for (const part of parts) {
18038
+ joined.set(part, at);
18039
+ at += part.length;
18040
+ }
18041
+ return joined;
18042
+ }
18043
+ write() {
18044
+ throw new PosixError(Errno.EISDIR);
18045
+ }
18046
+ seek(offset, whence) {
18047
+ if (whence === SEEK_SET && offset === 0) {
18048
+ this.index = 0;
18049
+ return 0;
18050
+ }
18051
+ throw new PosixError(Errno.EINVAL);
18052
+ }
18053
+ stat() {
18054
+ try {
18055
+ const st = this.vfs.stat(this.path, { cred: this.cred });
18056
+ return {
18057
+ ino: st.ino,
18058
+ mode: st.mode,
18059
+ size: st.size,
18060
+ uid: st.uid,
18061
+ gid: st.gid,
18062
+ nlink: st.nlink ?? 2,
18063
+ atimeMs: st.atimeMs,
18064
+ mtimeMs: st.mtimeMs,
18065
+ ctimeMs: st.ctimeMs
18066
+ };
18067
+ } catch (error) {
18068
+ throw toPosixError(error);
18069
+ }
18070
+ }
18071
+ readable() {
18072
+ return true;
18073
+ }
18074
+ writable() {
18075
+ return false;
18076
+ }
18077
+ whenReady() {
18078
+ return Promise.resolve();
18079
+ }
18080
+ close() {
18081
+ }
18082
+ };
18083
+ var PIPE_CAPACITY = 64 * 1024;
18084
+ var PipeBuffer = class {
18085
+ chunks = [];
18086
+ queued = 0;
18087
+ readers = 0;
18088
+ writers = 0;
18089
+ waiters = [];
18090
+ get available() {
18091
+ return this.queued;
18092
+ }
18093
+ get space() {
18094
+ return Math.max(0, PIPE_CAPACITY - this.queued);
18095
+ }
18096
+ push(data) {
18097
+ this.chunks.push(data);
18098
+ this.queued += data.length;
18099
+ this.wake();
18100
+ }
18101
+ take(length) {
18102
+ const out = new Uint8Array(Math.min(length, this.queued));
18103
+ let at = 0;
18104
+ while (at < out.length) {
18105
+ const head2 = this.chunks[0];
18106
+ const take = Math.min(head2.length, out.length - at);
18107
+ out.set(head2.subarray(0, take), at);
18108
+ at += take;
18109
+ if (take === head2.length) this.chunks.shift();
18110
+ else this.chunks[0] = head2.subarray(take);
18111
+ }
18112
+ this.queued -= out.length;
18113
+ this.wake();
18114
+ return out;
18115
+ }
18116
+ /** Anything that may have changed readiness for either end. */
18117
+ wake() {
18118
+ const waiting = this.waiters;
18119
+ this.waiters = [];
18120
+ for (const resolve2 of waiting) resolve2();
18121
+ }
18122
+ whenReady() {
18123
+ return new Promise((resolve2) => this.waiters.push(resolve2));
18124
+ }
18125
+ };
18126
+ var PipeReadEnd = class {
18127
+ constructor(buffer, flags) {
18128
+ this.buffer = buffer;
18129
+ this.flags = flags;
18130
+ buffer.readers += 1;
18131
+ }
18132
+ buffer;
18133
+ flags;
18134
+ kind = "pipe";
18135
+ read(length) {
18136
+ if (this.buffer.available === 0) {
18137
+ if (this.buffer.writers === 0) return new Uint8Array();
18138
+ throw new PosixError(Errno.EAGAIN);
18139
+ }
18140
+ return this.buffer.take(length);
18141
+ }
18142
+ write() {
18143
+ throw new PosixError(Errno.EBADF);
18144
+ }
18145
+ stat() {
18146
+ return pipeStat();
18147
+ }
18148
+ readable() {
18149
+ return this.buffer.available > 0 || this.buffer.writers === 0;
18150
+ }
18151
+ writable() {
18152
+ return false;
18153
+ }
18154
+ whenReady() {
18155
+ return this.buffer.whenReady();
18156
+ }
18157
+ close() {
18158
+ this.buffer.readers -= 1;
18159
+ this.buffer.wake();
18160
+ }
18161
+ };
18162
+ var PipeWriteEnd = class {
18163
+ constructor(buffer, flags) {
18164
+ this.buffer = buffer;
18165
+ this.flags = flags;
18166
+ buffer.writers += 1;
18167
+ }
18168
+ buffer;
18169
+ flags;
18170
+ kind = "pipe";
18171
+ read() {
18172
+ throw new PosixError(Errno.EBADF);
18173
+ }
18174
+ write(data) {
18175
+ if (this.buffer.readers === 0) throw new PosixError(Errno.EPIPE);
18176
+ const space = this.buffer.space;
18177
+ if (space === 0) throw new PosixError(Errno.EAGAIN);
18178
+ const accepted = data.subarray(0, space);
18179
+ this.buffer.push(accepted.slice());
18180
+ return accepted.length;
18181
+ }
18182
+ stat() {
18183
+ return pipeStat();
18184
+ }
18185
+ readable() {
18186
+ return false;
18187
+ }
18188
+ writable() {
18189
+ return this.buffer.space > 0 || this.buffer.readers === 0;
18190
+ }
18191
+ whenReady() {
18192
+ return this.buffer.whenReady();
18193
+ }
18194
+ close() {
18195
+ this.buffer.writers -= 1;
18196
+ this.buffer.wake();
18197
+ }
18198
+ };
18199
+ function pipeStat() {
18200
+ const now = Date.now();
18201
+ return { ino: 0, mode: 4480, size: 0, uid: 0, gid: 0, nlink: 1, atimeMs: now, mtimeMs: now, ctimeMs: now };
18202
+ }
18203
+ function createPipe(flags = 0) {
18204
+ const buffer = new PipeBuffer();
18205
+ return [new PipeReadEnd(buffer, flags | O_RDONLY), new PipeWriteEnd(buffer, flags | O_WRONLY)];
18206
+ }
18207
+ var StreamDescription = class {
18208
+ constructor(flags, sink, isTty = false) {
18209
+ this.flags = flags;
18210
+ this.sink = sink;
18211
+ this.isTty = isTty;
18212
+ }
18213
+ flags;
18214
+ sink;
18215
+ isTty;
18216
+ kind = "stream";
18217
+ queue = [];
18218
+ queued = 0;
18219
+ ended = false;
18220
+ waiters = [];
18221
+ /** Host side: give the guest more input. */
18222
+ push(data) {
18223
+ if (data.length === 0) return;
18224
+ this.queue.push(data);
18225
+ this.queued += data.length;
18226
+ this.wake();
18227
+ }
18228
+ /** Host side: no more input will arrive. */
18229
+ end() {
18230
+ this.ended = true;
18231
+ this.wake();
18232
+ }
18233
+ read(length) {
18234
+ if (this.queued === 0) {
18235
+ if (this.ended) return new Uint8Array();
18236
+ throw new PosixError(Errno.EAGAIN);
18237
+ }
18238
+ const out = new Uint8Array(Math.min(length, this.queued));
18239
+ let at = 0;
18240
+ while (at < out.length) {
18241
+ const head2 = this.queue[0];
18242
+ const take = Math.min(head2.length, out.length - at);
18243
+ out.set(head2.subarray(0, take), at);
18244
+ at += take;
18245
+ if (take === head2.length) this.queue.shift();
18246
+ else this.queue[0] = head2.subarray(take);
18247
+ }
18248
+ this.queued -= out.length;
18249
+ return out;
18250
+ }
18251
+ write(data) {
18252
+ if (!this.sink) throw new PosixError(Errno.EBADF);
18253
+ this.sink(data);
18254
+ return data.length;
18255
+ }
18256
+ stat() {
18257
+ const now = Date.now();
18258
+ return {
18259
+ ino: 0,
18260
+ mode: this.isTty ? 8592 : 4480,
18261
+ size: 0,
18262
+ uid: 0,
18263
+ gid: 0,
18264
+ nlink: 1,
18265
+ atimeMs: now,
18266
+ mtimeMs: now,
18267
+ ctimeMs: now
18268
+ };
18269
+ }
18270
+ readable() {
18271
+ return this.queued > 0 || this.ended;
18272
+ }
18273
+ writable() {
18274
+ return true;
18275
+ }
18276
+ whenReady() {
18277
+ return new Promise((resolve2) => this.waiters.push(resolve2));
18278
+ }
18279
+ close() {
18280
+ this.wake();
18281
+ }
18282
+ wake() {
18283
+ const waiting = this.waiters;
18284
+ this.waiters = [];
18285
+ for (const resolve2 of waiting) resolve2();
18286
+ }
18287
+ };
18288
+ function toPosixError(error) {
18289
+ if (error instanceof PosixError) return error;
18290
+ const code = error?.code;
18291
+ const message = error?.message;
18292
+ const mapped = {
18293
+ ENOENT: Errno.ENOENT,
18294
+ EEXIST: Errno.EEXIST,
18295
+ ENOTDIR: Errno.ENOTDIR,
18296
+ EISDIR: Errno.EISDIR,
18297
+ EACCES: Errno.EACCES,
18298
+ EPERM: Errno.EPERM,
18299
+ ENOTEMPTY: Errno.ENOTEMPTY,
18300
+ EINVAL: Errno.EINVAL,
18301
+ ELOOP: Errno.ELOOP,
18302
+ EXDEV: Errno.EXDEV,
18303
+ ENOSPC: Errno.ENOSPC,
18304
+ EMFILE: Errno.EMFILE,
18305
+ ENAMETOOLONG: Errno.ENAMETOOLONG,
18306
+ EBADF: Errno.EBADF,
18307
+ EPIPE: Errno.EPIPE
18308
+ };
18309
+ return new PosixError(code && mapped[code] ? mapped[code] : Errno.EIO, message);
18310
+ }
18311
+ var DescriptorTable = class _DescriptorTable {
18312
+ slots = /* @__PURE__ */ new Map();
18313
+ /** Descriptions this table shares with others, so close counts correctly. */
18314
+ static refs = /* @__PURE__ */ new WeakMap();
18315
+ /** Install at a chosen number, closing whatever was there. */
18316
+ set(fd, description, cloexec = false) {
18317
+ if (fd < 0) throw new PosixError(Errno.EBADF);
18318
+ const existing = this.slots.get(fd);
18319
+ if (existing) this.release(existing.description);
18320
+ this.slots.set(fd, { description, cloexec });
18321
+ _DescriptorTable.refs.set(description, (_DescriptorTable.refs.get(description) ?? 0) + 1);
18322
+ return fd;
18323
+ }
18324
+ /** Install at the lowest free number at or above `from`. */
18325
+ add(description, cloexec = false, from = 0) {
18326
+ let fd = from;
18327
+ while (this.slots.has(fd)) fd += 1;
18328
+ return this.set(fd, description, cloexec);
18329
+ }
18330
+ get(fd) {
18331
+ const slot = this.slots.get(fd);
18332
+ if (!slot) throw new PosixError(Errno.EBADF);
18333
+ return slot.description;
18334
+ }
18335
+ has(fd) {
18336
+ return this.slots.has(fd);
18337
+ }
18338
+ cloexec(fd) {
18339
+ const slot = this.slots.get(fd);
18340
+ if (!slot) throw new PosixError(Errno.EBADF);
18341
+ return slot.cloexec;
18342
+ }
18343
+ setCloexec(fd, value) {
18344
+ const slot = this.slots.get(fd);
18345
+ if (!slot) throw new PosixError(Errno.EBADF);
18346
+ slot.cloexec = value;
18347
+ }
18348
+ /** `dup`: a second name for one description, so the offset is shared. */
18349
+ dup(fd, from = 0) {
18350
+ return this.add(this.get(fd), false, from);
18351
+ }
18352
+ dup2(fd, target) {
18353
+ const description = this.get(fd);
18354
+ if (fd === target) return target;
18355
+ return this.set(target, description, false);
18356
+ }
18357
+ close(fd) {
18358
+ const slot = this.slots.get(fd);
18359
+ if (!slot) throw new PosixError(Errno.EBADF);
18360
+ this.slots.delete(fd);
18361
+ this.release(slot.description);
18362
+ }
18363
+ /** Everything a child inherits: a spawn keeps all but the close-on-exec ones. */
18364
+ inherit() {
18365
+ const child = new _DescriptorTable();
18366
+ for (const [fd, slot] of this.slots) {
18367
+ if (slot.cloexec) continue;
18368
+ child.set(fd, slot.description, false);
18369
+ }
18370
+ return child;
18371
+ }
18372
+ /** Drop every descriptor. Every resource has an owner and a cleanup path. */
18373
+ closeAll() {
18374
+ for (const fd of [...this.slots.keys()]) {
18375
+ try {
18376
+ this.close(fd);
18377
+ } catch {
18378
+ }
18379
+ }
18380
+ }
18381
+ get openCount() {
18382
+ return this.slots.size;
18383
+ }
18384
+ release(description) {
18385
+ const refs = (_DescriptorTable.refs.get(description) ?? 1) - 1;
18386
+ if (refs <= 0) {
18387
+ _DescriptorTable.refs.delete(description);
18388
+ description.close();
18389
+ } else {
18390
+ _DescriptorTable.refs.set(description, refs);
18391
+ }
18392
+ }
18393
+ };
18394
+ var FileService = class {
18395
+ constructor(vfs, cred) {
18396
+ this.vfs = vfs;
18397
+ this.cred = cred;
18398
+ }
18399
+ vfs;
18400
+ cred;
18401
+ openInodes = /* @__PURE__ */ new Map();
18402
+ open(path, flags, mode = 438) {
18403
+ const exists = this.vfs.lexists(path);
18404
+ if (flags & O_CREAT2 && flags & O_EXCL2 && exists) throw new PosixError(Errno.EEXIST);
18405
+ if (!exists) {
18406
+ if (!(flags & O_CREAT2)) throw new PosixError(Errno.ENOENT);
18407
+ if (accessMode(flags) === O_RDONLY) throw new PosixError(Errno.EACCES);
18408
+ try {
18409
+ this.vfs.writeFile(path, new Uint8Array(), { cred: this.cred, mode });
18410
+ } catch (error) {
18411
+ throw toPosixError(error);
18412
+ }
18413
+ }
18414
+ let stat2;
18415
+ try {
18416
+ stat2 = this.vfs.stat(path, { cred: this.cred });
18417
+ } catch (error) {
18418
+ throw toPosixError(error);
18419
+ }
18420
+ if (stat2.isDirectory()) {
18421
+ if (accessMode(flags) !== O_RDONLY) throw new PosixError(Errno.EISDIR);
18422
+ return new DirectoryDescription(path, flags, this.vfs, this.cred);
18423
+ }
18424
+ if (flags & O_DIRECTORY2) throw new PosixError(Errno.ENOTDIR);
18425
+ const inode = this.inodeFor(stat2.ino, path);
18426
+ const description = new FileDescription(inode, flags);
18427
+ if (flags & O_TRUNC2 && accessMode(flags) !== O_RDONLY) description.truncate(0);
18428
+ return description;
18429
+ }
18430
+ /**
18431
+ * Remove a name.
18432
+ *
18433
+ * If something still has the file open, its contents move into the open
18434
+ * description first: POSIX guarantees a reader keeps reading, and a volume
18435
+ * that only knows about paths cannot provide that on its own.
18436
+ */
18437
+ unlink(path) {
18438
+ let ino = null;
18439
+ try {
18440
+ ino = this.vfs.lstat(path).ino;
18441
+ } catch (error) {
18442
+ throw toPosixError(error);
18443
+ }
18444
+ const inode = ino === null ? void 0 : this.openInodes.get(ino);
18445
+ if (inode && inode.refs > 0 && inode.path === path) inode.detach();
18446
+ try {
18447
+ this.vfs.unlink(path, this.cred);
18448
+ } catch (error) {
18449
+ throw toPosixError(error);
18450
+ }
18451
+ if (inode && inode.refs <= 0) this.openInodes.delete(ino);
18452
+ }
18453
+ /** Rename a name. Anything already open follows the file to its new name. */
18454
+ rename(from, to) {
18455
+ let ino = null;
18456
+ try {
18457
+ ino = this.vfs.lstat(from).ino;
18458
+ } catch {
18459
+ }
18460
+ try {
18461
+ this.vfs.rename(from, to, this.cred);
18462
+ } catch (error) {
18463
+ throw toPosixError(error);
18464
+ }
18465
+ if (ino === null) return;
18466
+ const inode = this.openInodes.get(ino);
18467
+ if (inode) inode.path = to;
18468
+ }
18469
+ /** One {@link Inode} per file identity, shared by every open description. */
18470
+ inodeFor(ino, path) {
18471
+ const existing = this.openInodes.get(ino);
18472
+ if (existing) {
18473
+ existing.path = path;
18474
+ return existing;
18475
+ }
18476
+ const created = new Inode(ino, path, this.vfs, this.cred);
18477
+ this.openInodes.set(ino, created);
18478
+ return created;
18479
+ }
18480
+ };
18481
+
18482
+ // src/runtime/python/protocol.ts
18483
+ var ProtocolError = class extends Error {
18484
+ code = "ERR_SBX_ABI_PROTOCOL";
18485
+ };
18486
+ function decodeRequest(frame) {
18487
+ if (frame.length < SBX_REQUEST_HEADER_BYTES) throw new ProtocolError("short request frame");
18488
+ const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
18489
+ const header = {
18490
+ version: view.getUint16(0, true),
18491
+ op: view.getUint16(2, true),
18492
+ requestId: view.getUint32(4, true),
18493
+ generation: view.getUint32(8, true)
18494
+ };
18495
+ const length = view.getUint32(12, true);
18496
+ if (frame.length - SBX_REQUEST_HEADER_BYTES < length) {
18497
+ throw new ProtocolError(`truncated payload for ${OP_NAMES[header.op] ?? header.op}`);
18498
+ }
18499
+ return {
18500
+ header,
18501
+ payload: frame.subarray(SBX_REQUEST_HEADER_BYTES, SBX_REQUEST_HEADER_BYTES + length)
18502
+ };
18503
+ }
18504
+ function encodeResponse(header, status, payload) {
18505
+ const frame = new Uint8Array(SBX_RESPONSE_HEADER_BYTES + payload.length);
18506
+ const view = new DataView(frame.buffer);
18507
+ view.setUint16(0, header.version, true);
18508
+ view.setUint16(2, header.op, true);
18509
+ view.setUint32(4, header.requestId, true);
18510
+ view.setUint32(8, header.generation, true);
18511
+ view.setInt32(12, status, true);
18512
+ view.setUint32(16, payload.length, true);
18513
+ frame.set(payload, SBX_RESPONSE_HEADER_BYTES);
18514
+ return frame;
18515
+ }
18516
+ var Reader = class {
18517
+ at = 0;
18518
+ view;
18519
+ bytes;
18520
+ constructor(bytes2) {
18521
+ this.bytes = bytes2;
18522
+ this.view = new DataView(bytes2.buffer, bytes2.byteOffset, bytes2.byteLength);
18523
+ }
18524
+ need(count) {
18525
+ if (this.at + count > this.bytes.length) throw new ProtocolError("payload underrun");
18526
+ const offset = this.at;
18527
+ this.at += count;
18528
+ return offset;
18529
+ }
18530
+ i32() {
18531
+ return this.view.getInt32(this.need(4), true);
18532
+ }
18533
+ u32() {
18534
+ return this.view.getUint32(this.need(4), true);
18535
+ }
18536
+ i64() {
18537
+ return Number(this.view.getBigInt64(this.need(8), true));
18538
+ }
18539
+ u64() {
18540
+ return Number(this.view.getBigUint64(this.need(8), true));
18541
+ }
18542
+ bytes32() {
18543
+ const length = this.u32();
18544
+ const offset = this.need(length);
18545
+ return this.bytes.subarray(offset, offset + length);
18546
+ }
18547
+ string() {
18548
+ return new TextDecoder().decode(this.bytes32());
18549
+ }
18550
+ get remaining() {
18551
+ return this.bytes.length - this.at;
18552
+ }
18553
+ };
18554
+ var Writer = class {
18555
+ parts = [];
18556
+ i32(value) {
18557
+ const b = new Uint8Array(4);
18558
+ new DataView(b.buffer).setInt32(0, value, true);
18559
+ this.parts.push(b);
18560
+ return this;
18561
+ }
18562
+ u32(value) {
18563
+ const b = new Uint8Array(4);
18564
+ new DataView(b.buffer).setUint32(0, value >>> 0, true);
18565
+ this.parts.push(b);
18566
+ return this;
18567
+ }
18568
+ i64(value) {
18569
+ const b = new Uint8Array(8);
18570
+ new DataView(b.buffer).setBigInt64(0, BigInt(Math.trunc(value)), true);
18571
+ this.parts.push(b);
18572
+ return this;
18573
+ }
18574
+ u64(value) {
18575
+ const b = new Uint8Array(8);
18576
+ new DataView(b.buffer).setBigUint64(0, BigInt(Math.trunc(value)), true);
18577
+ this.parts.push(b);
18578
+ return this;
18579
+ }
18580
+ bytes32(value) {
18581
+ this.u32(value.length);
18582
+ this.parts.push(value);
18583
+ return this;
18584
+ }
18585
+ string(value) {
18586
+ return this.bytes32(new TextEncoder().encode(value));
18587
+ }
18588
+ finish() {
18589
+ const total = this.parts.reduce((sum, part) => sum + part.length, 0);
18590
+ const out = new Uint8Array(total);
18591
+ let at = 0;
18592
+ for (const part of this.parts) {
18593
+ out.set(part, at);
18594
+ at += part.length;
18595
+ }
18596
+ return out;
18597
+ }
18598
+ };
18599
+ function failure(errno) {
18600
+ return -Math.abs(errno);
18601
+ }
18602
+
18603
+ // src/runtime/python/syscall-server.ts
18604
+ var MAX_TRANSFER = 8 * 1024 * 1024;
18605
+ function createHostAbiServer(proc) {
18606
+ const files = new FileService(proc.vfs, proc.cred);
18607
+ const started = Date.now();
18608
+ const monotonicBase = typeof performance === "object" ? performance.now() : 0;
18609
+ return async (frame) => {
18610
+ const { header, payload } = decodeRequest(frame);
18611
+ if (header.version !== SBX_HOST_ABI_VERSION) {
18612
+ return encodeResponse(header, failure(Errno.EPROTO), new Uint8Array());
18613
+ }
18614
+ if (header.generation !== proc.generation) {
18615
+ return encodeResponse(header, failure(Errno.ESRCH), new Uint8Array());
18616
+ }
18617
+ try {
18618
+ const { status, payload: out } = await dispatch(header, new Reader(payload));
18619
+ return encodeResponse(header, status, out ?? new Uint8Array());
18620
+ } catch (error) {
18621
+ const posix = toPosixError(error);
18622
+ return encodeResponse(header, failure(posix.errno), new Uint8Array());
18623
+ }
18624
+ };
18625
+ async function dispatch(header, r) {
18626
+ switch (header.op) {
18627
+ case Op.handshake: {
18628
+ const w = new Writer().u32(SBX_HOST_ABI_VERSION).u32(CAPABILITIES.length);
18629
+ for (const capability of CAPABILITIES) {
18630
+ w.string(capability).u32(implemented(capability) ? 1 : 0);
18631
+ }
18632
+ return { status: 0, payload: w.finish() };
18633
+ }
18634
+ /* ------------------------------------------------------------ files */
18635
+ case Op.openat: {
18636
+ const path = resolve2(r.string());
18637
+ const flags = r.u32();
18638
+ const mode = r.u32();
18639
+ const description = files.open(path, flags, mode);
18640
+ return { status: proc.table.add(description, (flags & O_CLOEXEC) !== 0) };
18641
+ }
18642
+ case Op.close: {
18643
+ proc.table.close(r.i32());
18644
+ return { status: 0 };
18645
+ }
18646
+ case Op.read: {
18647
+ const description = proc.table.get(r.i32());
18648
+ const length = clamp(r.u32());
18649
+ const data = await readBlocking(description, length);
18650
+ return { status: data.length, payload: data };
18651
+ }
18652
+ case Op.write: {
18653
+ const description = proc.table.get(r.i32());
18654
+ const data = r.bytes32();
18655
+ const written = await writeBlocking(description, data);
18656
+ return { status: written };
18657
+ }
18658
+ case Op.pread: {
18659
+ const description = proc.table.get(r.i32());
18660
+ const length = clamp(r.u32());
18661
+ const offset = r.u64();
18662
+ if (!description.pread) throw new PosixError(Errno.ESPIPE);
18663
+ const data = description.pread(length, offset);
18664
+ return { status: data.length, payload: data };
18665
+ }
18666
+ case Op.pwrite: {
18667
+ const description = proc.table.get(r.i32());
18668
+ const data = r.bytes32();
18669
+ const offset = r.u64();
18670
+ if (!description.pwrite) throw new PosixError(Errno.ESPIPE);
18671
+ return { status: description.pwrite(data, offset) };
18672
+ }
18673
+ case Op.lseek: {
18674
+ const description = proc.table.get(r.i32());
18675
+ const offset = r.i64();
18676
+ const whence = r.u32();
18677
+ if (!description.seek) throw new PosixError(Errno.ESPIPE);
18678
+ return { status: 0, payload: new Writer().i64(description.seek(offset, whence)).finish() };
18679
+ }
18680
+ case Op.fstat: {
18681
+ return { status: 0, payload: encodeStat(proc.table.get(r.i32()).stat()) };
18682
+ }
18683
+ case Op.statat: {
18684
+ const path = resolve2(r.string());
18685
+ const followLinks = r.u32() !== 0;
18686
+ try {
18687
+ const st = followLinks ? proc.vfs.stat(path, { cred: proc.cred }) : proc.vfs.lstat(path);
18688
+ return {
18689
+ status: 0,
18690
+ payload: encodeStat({
18691
+ ino: st.ino,
18692
+ mode: st.mode,
18693
+ size: st.size,
18694
+ uid: st.uid,
18695
+ gid: st.gid,
18696
+ nlink: st.nlink ?? 1,
18697
+ atimeMs: st.atimeMs,
18698
+ mtimeMs: st.mtimeMs,
18699
+ ctimeMs: st.ctimeMs
18700
+ })
18701
+ };
18702
+ } catch (error) {
18703
+ throw toPosixError(error);
18704
+ }
18705
+ }
18706
+ case Op.ftruncate: {
18707
+ const description = proc.table.get(r.i32());
18708
+ const length = r.u64();
18709
+ if (!description.truncate) throw new PosixError(Errno.EINVAL);
18710
+ description.truncate(length);
18711
+ return { status: 0 };
18712
+ }
18713
+ case Op.renameat: {
18714
+ files.rename(resolve2(r.string()), resolve2(r.string()));
18715
+ return { status: 0 };
18716
+ }
18717
+ case Op.unlinkat: {
18718
+ const path = resolve2(r.string());
18719
+ const removeDirectory = r.u32() !== 0;
18720
+ if (removeDirectory) {
18721
+ try {
18722
+ proc.vfs.rmdir(path, proc.cred);
18723
+ } catch (error) {
18724
+ throw toPosixError(error);
18725
+ }
18726
+ } else {
18727
+ files.unlink(path);
18728
+ }
18729
+ return { status: 0 };
18730
+ }
18731
+ case Op.mkdirat: {
18732
+ const path = resolve2(r.string());
18733
+ const mode = r.u32();
18734
+ try {
18735
+ proc.vfs.mkdir(path, { mode, cred: proc.cred });
18736
+ } catch (error) {
18737
+ throw toPosixError(error);
18738
+ }
18739
+ return { status: 0 };
18740
+ }
18741
+ case Op.readlinkat: {
18742
+ try {
18743
+ const target = proc.vfs.readlink(resolve2(r.string()), proc.cred);
18744
+ return { status: 0, payload: new Writer().string(target).finish() };
18745
+ } catch (error) {
18746
+ throw toPosixError(error);
18747
+ }
18748
+ }
18749
+ case Op.symlinkat: {
18750
+ const target = r.string();
18751
+ try {
18752
+ proc.vfs.symlink(target, resolve2(r.string()), proc.cred);
18753
+ } catch (error) {
18754
+ throw toPosixError(error);
18755
+ }
18756
+ return { status: 0 };
18757
+ }
18758
+ case Op.getdents: {
18759
+ const description = proc.table.get(r.i32());
18760
+ if (description.kind !== "dir") throw new PosixError(Errno.ENOTDIR);
18761
+ const data = description.read(clamp(r.u32()));
18762
+ return { status: data.length, payload: data };
18763
+ }
18764
+ case Op.fsync: {
18765
+ proc.table.get(r.i32());
18766
+ return { status: 0 };
18767
+ }
18768
+ /* ------------------------------------------------------ descriptors */
18769
+ case Op.dup:
18770
+ return { status: proc.table.dup(r.i32(), r.u32()) };
18771
+ case Op.dup2:
18772
+ return { status: proc.table.dup2(r.i32(), r.i32()) };
18773
+ case Op.get_flags: {
18774
+ const fd = r.i32();
18775
+ const description = proc.table.get(fd);
18776
+ return { status: 0, payload: new Writer().u32(description.flags).u32(proc.table.cloexec(fd) ? 1 : 0).finish() };
18777
+ }
18778
+ case Op.set_flags: {
18779
+ const fd = r.i32();
18780
+ const flags = r.u32();
18781
+ const cloexec = r.u32();
18782
+ const description = proc.table.get(fd);
18783
+ description.flags = accessMode(description.flags) | flags & -4;
18784
+ proc.table.setCloexec(fd, cloexec !== 0);
18785
+ return { status: 0 };
18786
+ }
18787
+ /* ------------------------------------------------------------ pipes */
18788
+ case Op.pipe: {
18789
+ const flags = r.u32();
18790
+ const [readEnd, writeEnd] = createPipe(flags & O_NONBLOCK);
18791
+ const cloexec = (flags & O_CLOEXEC) !== 0;
18792
+ const readFd = proc.table.add(readEnd, cloexec);
18793
+ const writeFd = proc.table.add(writeEnd, cloexec);
18794
+ return { status: 0, payload: new Writer().i32(readFd).i32(writeFd).finish() };
18795
+ }
18796
+ /* -------------------------------------------------------- readiness */
18797
+ case Op.poll: {
18798
+ const count = r.u32();
18799
+ const requests = [];
18800
+ for (let i = 0; i < count; i += 1) requests.push({ fd: r.i32(), events: r.u32() });
18801
+ const timeoutMs = r.i64();
18802
+ const ready = await poll(requests, timeoutMs);
18803
+ const w = new Writer().u32(ready.length);
18804
+ for (const entry of ready) w.i32(entry.fd).u32(entry.revents);
18805
+ return { status: ready.length, payload: w.finish() };
18806
+ }
18807
+ /* ------------------------------------------------------------- time */
18808
+ case Op.clock_gettime: {
18809
+ const monotonic = r.u32() !== 0;
18810
+ const now = monotonic ? (typeof performance === "object" ? performance.now() : Date.now() - started) - monotonicBase : Date.now();
18811
+ const nanos = Math.round(now * 1e6);
18812
+ return { status: 0, payload: new Writer().i64(Math.floor(nanos / 1e9)).u32(nanos % 1e9).finish() };
18813
+ }
18814
+ case Op.sleep: {
18815
+ const nanos = r.u64();
18816
+ await sleep(nanos / 1e6);
18817
+ return { status: proc.signal.aborted ? failure(Errno.EINTR) : 0 };
18818
+ }
18819
+ /* --------------------------------------------------------- identity */
18820
+ case Op.getpid: {
18821
+ const w = new Writer().i32(proc.pid).i32(proc.ppid).u32(proc.cred.uid).u32(proc.cred.gid).u32(proc.cred.umask ?? 18);
18822
+ return { status: 0, payload: w.finish() };
18823
+ }
18824
+ case Op.getcwd:
18825
+ return { status: 0, payload: new Writer().string(proc.cwd).finish() };
18826
+ case Op.chdir: {
18827
+ const path = resolve2(r.string());
18828
+ let st;
18829
+ try {
18830
+ st = proc.vfs.stat(path, { cred: proc.cred });
18831
+ } catch (error) {
18832
+ throw toPosixError(error);
18833
+ }
18834
+ if (!st.isDirectory()) throw new PosixError(Errno.ENOTDIR);
18835
+ proc.cwd = path;
18836
+ return { status: 0 };
18837
+ }
18838
+ case Op.environ: {
18839
+ const joined = Object.entries(proc.env).map(([key, value]) => `${key}=${value}`).join("\0");
18840
+ return { status: 0, payload: new Writer().string(joined).finish() };
18841
+ }
18842
+ /* ---------------------------------------------------------- entropy */
18843
+ case Op.getrandom: {
18844
+ const length = Math.min(r.u32(), 65536);
18845
+ const bytes2 = new Uint8Array(length);
18846
+ crypto.getRandomValues(bytes2);
18847
+ return { status: length, payload: bytes2 };
18848
+ }
18849
+ default:
18850
+ return { status: failure(Errno.ENOSYS) };
18851
+ }
18852
+ }
18853
+ function implemented(capability) {
18854
+ return ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy"].includes(capability);
18855
+ }
18856
+ function resolve2(path) {
18857
+ if (path.startsWith("/")) return proc.vfs.resolvePath(path);
18858
+ return proc.vfs.resolvePath(`${proc.cwd.replace(/\/$/, "")}/${path}`);
18859
+ }
18860
+ function clamp(length) {
18861
+ return Math.min(length, MAX_TRANSFER);
18862
+ }
18863
+ async function readBlocking(description, length) {
18864
+ for (; ; ) {
18865
+ try {
18866
+ return description.read(length);
18867
+ } catch (error) {
18868
+ const posix = toPosixError(error);
18869
+ if (posix.errno !== Errno.EAGAIN || description.flags & O_NONBLOCK) throw posix;
18870
+ }
18871
+ await Promise.race([description.whenReady(), aborted()]);
18872
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18873
+ }
18874
+ }
18875
+ async function writeBlocking(description, data) {
18876
+ for (; ; ) {
18877
+ try {
18878
+ return description.write(data);
18879
+ } catch (error) {
18880
+ const posix = toPosixError(error);
18881
+ if (posix.errno !== Errno.EAGAIN || description.flags & O_NONBLOCK) throw posix;
18882
+ }
18883
+ await Promise.race([description.whenReady(), aborted()]);
18884
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18885
+ }
18886
+ }
18887
+ const POLLIN = 1;
18888
+ const POLLOUT = 4;
18889
+ const POLLHUP = 16;
18890
+ async function poll(requests, timeoutMs) {
18891
+ const deadline = timeoutMs < 0 ? Infinity : Date.now() + timeoutMs;
18892
+ for (; ; ) {
18893
+ const ready = [];
18894
+ const waits = [];
18895
+ for (const { fd, events } of requests) {
18896
+ let description;
18897
+ try {
18898
+ description = proc.table.get(fd);
18899
+ } catch {
18900
+ ready.push({ fd, revents: POLLHUP });
18901
+ continue;
18902
+ }
18903
+ let revents = 0;
18904
+ if (events & POLLIN && description.readable()) revents |= POLLIN;
18905
+ if (events & POLLOUT && description.writable()) revents |= POLLOUT;
18906
+ if (revents) ready.push({ fd, revents });
18907
+ else waits.push(description.whenReady());
18908
+ }
18909
+ if (ready.length > 0) return ready;
18910
+ const remaining = deadline - Date.now();
18911
+ if (remaining <= 0) return [];
18912
+ await Promise.race([...waits, sleep(Math.min(remaining, 50)), aborted()]);
18913
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
18914
+ }
18915
+ }
18916
+ function aborted() {
18917
+ if (proc.signal.aborted) return Promise.resolve();
18918
+ return new Promise((resolve3) => proc.signal.addEventListener("abort", () => resolve3(), { once: true }));
18919
+ }
18920
+ }
18921
+ function sleep(ms) {
18922
+ return new Promise((resolve2) => setTimeout(resolve2, Math.max(0, ms)));
18923
+ }
18924
+ function encodeStat(st) {
18925
+ 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();
18926
+ }
18927
+
18928
+ // src/runtime/python/sync-transport.ts
18929
+ var STATE = 0;
18930
+ var LENGTH = 1;
18931
+ var MORE = 2;
18932
+ var CONTROL_WORDS = 4;
18933
+ var STATE_REQUEST = 1;
18934
+ var STATE_RESPONSE = 2;
18935
+ var STATE_CONTINUE = 3;
18936
+ var STATE_CLOSED = 4;
18937
+ function createHostTransportBuffers(windowBytes = 1 << 20) {
18938
+ return {
18939
+ control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
18940
+ data: new SharedArrayBuffer(windowBytes)
18941
+ };
18942
+ }
18943
+ function syncTransportUnavailableReason() {
18944
+ if (typeof globalThis.crossOriginIsolated === "boolean" && !globalThis.crossOriginIsolated) {
18945
+ return "the page is not cross-origin isolated. Serve it with Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.";
18946
+ }
18947
+ if (typeof SharedArrayBuffer !== "function") return "SharedArrayBuffer is unavailable in this host.";
18948
+ if (typeof Atomics !== "object" || typeof Atomics.wait !== "function") return "Atomics.wait is unavailable in this host.";
18949
+ return null;
18950
+ }
18951
+ var HostCallServer = class {
18952
+ control;
18953
+ data;
18954
+ capacity;
18955
+ handle;
18956
+ onFault;
18957
+ incoming = [];
18958
+ outgoing = null;
18959
+ sent = 0;
18960
+ closed = false;
18961
+ constructor(buffers, handle, onFault = () => {
18962
+ }) {
18963
+ this.control = new Int32Array(buffers.control);
18964
+ this.data = new Uint8Array(buffers.data);
18965
+ this.capacity = this.data.length;
18966
+ this.handle = handle;
18967
+ this.onFault = onFault;
18968
+ }
18969
+ /** Call on each wake message from the client. */
18970
+ async pump() {
18971
+ if (this.closed) return;
18972
+ const state = Atomics.load(this.control, STATE);
18973
+ if (state === STATE_REQUEST) {
18974
+ const size = Atomics.load(this.control, LENGTH);
18975
+ this.incoming.push(this.data.slice(0, size));
18976
+ if (Atomics.load(this.control, MORE) === 1) {
18977
+ this.publish(STATE_CONTINUE);
18978
+ return;
18979
+ }
18980
+ const request = concat3(this.incoming);
18981
+ this.incoming = [];
18982
+ let response;
18983
+ try {
18984
+ response = await this.handle(request);
18985
+ } catch (error) {
18986
+ this.onFault(error);
18987
+ this.close();
18988
+ return;
18989
+ }
18990
+ if (this.closed) return;
18991
+ this.outgoing = response;
18992
+ this.sent = 0;
18993
+ this.sendWindow();
18994
+ return;
18995
+ }
18996
+ if (state === STATE_CONTINUE) this.sendWindow();
18997
+ }
18998
+ /** Release a blocked client — container teardown, or the worker being killed. */
18999
+ close() {
19000
+ if (this.closed) return;
19001
+ this.closed = true;
19002
+ Atomics.store(this.control, STATE, STATE_CLOSED);
19003
+ Atomics.notify(this.control, STATE);
19004
+ }
19005
+ sendWindow() {
19006
+ const payload = this.outgoing ?? new Uint8Array();
19007
+ const size = Math.min(this.capacity, payload.length - this.sent);
19008
+ this.data.set(payload.subarray(this.sent, this.sent + size), 0);
19009
+ this.sent += size;
19010
+ Atomics.store(this.control, LENGTH, size);
19011
+ Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
19012
+ this.publish(STATE_RESPONSE);
19013
+ }
19014
+ publish(state) {
19015
+ Atomics.store(this.control, STATE, state);
19016
+ Atomics.notify(this.control, STATE);
19017
+ }
19018
+ };
19019
+ function concat3(parts) {
19020
+ if (parts.length === 1) return parts[0];
19021
+ const total = parts.reduce((sum, part) => sum + part.length, 0);
19022
+ const joined = new Uint8Array(total);
19023
+ let at = 0;
19024
+ for (const part of parts) {
19025
+ joined.set(part, at);
19026
+ at += part.length;
19027
+ }
19028
+ return joined;
19029
+ }
19030
+
19031
+ // src/runtime/worker-host.ts
19032
+ function defaultWorkerUrl() {
19033
+ return new URL("./worker-entry.js", import.meta.url);
19034
+ }
19035
+ function hasDomWorker() {
19036
+ return typeof Worker === "function";
19037
+ }
19038
+ async function startRuntimeWorker(options = {}) {
19039
+ const url = options.url ?? defaultWorkerUrl();
19040
+ const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
19041
+ try {
19042
+ await new Promise((resolve2, reject) => {
19043
+ const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
19044
+ worker.onMessage((message) => {
19045
+ if (message?.type === "sandboxedjs:ready") {
19046
+ clearTimeout(timer);
19047
+ resolve2();
19048
+ }
19049
+ });
19050
+ worker.onError((error) => {
19051
+ clearTimeout(timer);
19052
+ reject(error instanceof Error ? error : new Error(String(error)));
19053
+ });
19054
+ });
19055
+ } catch (error) {
19056
+ try {
19057
+ await worker.terminate();
19058
+ } catch {
19059
+ }
19060
+ throw error;
19061
+ }
19062
+ return worker;
19063
+ }
19064
+ async function startDomWorker(url) {
19065
+ const worker = new Worker(url, { type: "module" });
19066
+ const listeners = [];
19067
+ const errors = [];
19068
+ worker.addEventListener("message", (event) => {
19069
+ for (const listener of listeners) listener(event.data);
19070
+ });
19071
+ worker.addEventListener("error", (event) => {
19072
+ const detail = event.message || "the runtime worker failed to load";
19073
+ for (const listener of errors) listener(new Error(detail));
19074
+ });
19075
+ return {
19076
+ postMessage: (message) => worker.postMessage(message),
19077
+ onMessage: (listener) => {
19078
+ listeners.push(listener);
19079
+ },
19080
+ onError: (listener) => {
19081
+ errors.push(listener);
19082
+ },
19083
+ terminate: () => worker.terminate()
19084
+ };
19085
+ }
19086
+ async function startNodeWorker(url, workerData) {
19087
+ const specifier = ["node", "worker_threads"].join(":");
19088
+ const { Worker: NodeWorker } = await import(
19089
+ /* @vite-ignore */
19090
+ /* webpackIgnore: true */
19091
+ specifier
19092
+ );
19093
+ const worker = new NodeWorker(url, { workerData });
19094
+ return {
19095
+ postMessage: (message) => worker.postMessage(message),
19096
+ onMessage: (listener) => worker.on("message", listener),
19097
+ onError: (listener) => worker.on("error", listener),
19098
+ terminate: () => worker.terminate()
19099
+ };
19100
+ }
19101
+
19102
+ // src/runtime/python/supervisor.ts
19103
+ var nextGeneration = 1;
19104
+ async function startPythonProcess(options) {
19105
+ const unavailable2 = syncTransportUnavailableReason();
19106
+ if (unavailable2) {
19107
+ throw new Error(`The SandboxedJs Python runtime needs blocking worker calls: ${unavailable2}`);
19108
+ }
19109
+ const generation = nextGeneration++;
19110
+ const stdin = new StreamDescription(O_RDONLY, null, options.isTty);
19111
+ const stdout = new StreamDescription(O_WRONLY, options.onStdout, options.isTty);
19112
+ const stderr = new StreamDescription(O_WRONLY, options.onStderr, options.isTty);
19113
+ const table = new DescriptorTable();
19114
+ table.set(0, stdin);
19115
+ table.set(1, stdout);
19116
+ table.set(2, stderr);
19117
+ const controller = new AbortController();
19118
+ const abort = () => controller.abort();
19119
+ options.signal.addEventListener("abort", abort, { once: true });
19120
+ const handle = createHostAbiServer({
19121
+ pid: options.pid,
19122
+ ppid: options.ppid,
19123
+ generation,
19124
+ vfs: options.vfs,
19125
+ cred: options.cred,
19126
+ table,
19127
+ env: options.env,
19128
+ cwd: options.cwd,
19129
+ signal: controller.signal
19130
+ });
19131
+ const buffers = createHostTransportBuffers();
19132
+ let fault = null;
19133
+ const server = new HostCallServer(buffers, handle, (error) => {
19134
+ fault = error;
19135
+ });
19136
+ let worker = null;
19137
+ let settle = () => {
19138
+ };
19139
+ let blame = () => {
19140
+ };
19141
+ const finished = new Promise((resolve2, reject) => {
19142
+ settle = resolve2;
19143
+ blame = reject;
19144
+ });
19145
+ let done = false;
19146
+ const cleanup = () => {
19147
+ if (done) return;
19148
+ done = true;
19149
+ options.signal.removeEventListener("abort", abort);
19150
+ server.close();
19151
+ table.closeAll();
19152
+ try {
19153
+ worker?.terminate();
19154
+ } catch {
19155
+ }
19156
+ };
19157
+ worker = await startRuntimeWorker({ url: options.workerUrl });
19158
+ worker.onMessage((message) => {
19159
+ const detail = message;
19160
+ if (detail?.type === "wake") {
19161
+ void server.pump();
19162
+ return;
19163
+ }
19164
+ if (detail?.type === "exit") {
19165
+ cleanup();
19166
+ settle(detail.code ?? 0);
19167
+ return;
19168
+ }
19169
+ if (detail?.type === "failed") {
19170
+ cleanup();
19171
+ blame(new Error(detail.message ?? "the Python process failed to start"));
19172
+ }
19173
+ });
19174
+ worker.onError((error) => {
19175
+ cleanup();
19176
+ blame(error instanceof Error ? error : new Error(String(error)));
19177
+ });
19178
+ controller.signal.addEventListener("abort", () => {
19179
+ if (done) return;
19180
+ cleanup();
19181
+ settle(130);
19182
+ });
19183
+ worker.postMessage({
19184
+ type: "start",
19185
+ buffers,
19186
+ generation,
19187
+ moduleUrl: options.manifest.artifacts.moduleUrl,
19188
+ argv: options.argv,
19189
+ env: options.env,
19190
+ cwd: options.cwd,
19191
+ pid: options.pid,
19192
+ ppid: options.ppid,
19193
+ mounts: options.mounts,
19194
+ isTty: options.isTty
19195
+ });
19196
+ return {
19197
+ write: (data) => stdin.push(data),
19198
+ endInput: () => stdin.end(),
19199
+ kill: () => controller.abort(),
19200
+ wait: async () => {
19201
+ const code = await finished;
19202
+ if (fault) throw fault;
19203
+ return code;
19204
+ }
19205
+ };
19206
+ }
19207
+ var END_OF_CENTRAL_DIRECTORY = 101010256;
19208
+ var CENTRAL_FILE_HEADER = 33639248;
19209
+ var ZIP64_END_LOCATOR = 117853008;
19210
+ var ZIP64_END_OF_CENTRAL_DIRECTORY = 101075792;
19211
+ var ZipError = class extends Error {
19212
+ code = "ERR_ZIP";
19213
+ };
19214
+ function readZip(archive) {
19215
+ const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
19216
+ const end = findEndOfCentralDirectory(archive);
19217
+ let entryCount = view.getUint16(end + 10, true);
19218
+ let directoryOffset = view.getUint32(end + 16, true);
19219
+ if (entryCount === 65535 || directoryOffset === 4294967295) {
19220
+ const locator = findSignature(archive, ZIP64_END_LOCATOR, end);
19221
+ if (locator < 0) throw new ZipError("zip64 archive without an end locator");
19222
+ const zip64End = Number(view.getBigUint64(locator + 8, true));
19223
+ if (view.getUint32(zip64End, true) !== ZIP64_END_OF_CENTRAL_DIRECTORY) {
19224
+ throw new ZipError("zip64 end record not found where the locator says");
19225
+ }
19226
+ entryCount = Number(view.getBigUint64(zip64End + 32, true));
19227
+ directoryOffset = Number(view.getBigUint64(zip64End + 48, true));
19228
+ }
19229
+ const entries = [];
19230
+ let at = directoryOffset;
19231
+ for (let i = 0; i < entryCount; i += 1) {
19232
+ if (view.getUint32(at, true) !== CENTRAL_FILE_HEADER) {
19233
+ throw new ZipError(`central directory entry ${i} has a bad signature`);
19234
+ }
19235
+ const flags = view.getUint16(at + 8, true);
19236
+ if (flags & 1) throw new ZipError("encrypted archives are not supported");
19237
+ const method = view.getUint16(at + 10, true);
19238
+ const compressedSize = view.getUint32(at + 20, true);
19239
+ const uncompressedSize = view.getUint32(at + 24, true);
19240
+ const nameLength = view.getUint16(at + 28, true);
19241
+ const extraLength = view.getUint16(at + 30, true);
19242
+ const commentLength = view.getUint16(at + 32, true);
19243
+ const externalAttributes = view.getUint32(at + 38, true);
19244
+ const localOffset = view.getUint32(at + 42, true);
19245
+ const name = new TextDecoder().decode(archive.subarray(at + 46, at + 46 + nameLength));
19246
+ entries.push({
19247
+ name,
19248
+ /* The high 16 bits are the Unix mode when the archive was made on Unix.
19249
+ * pip relies on it for console scripts, which have to stay executable. */
19250
+ mode: externalAttributes >>> 16,
19251
+ isDirectory: name.endsWith("/"),
19252
+ data: () => extract(archive, view, localOffset, method, compressedSize, uncompressedSize, name)
19253
+ });
19254
+ at += 46 + nameLength + extraLength + commentLength;
19255
+ }
19256
+ return entries;
19257
+ }
19258
+ function extract(archive, view, localOffset, method, compressedSize, uncompressedSize, name) {
19259
+ const nameLength = view.getUint16(localOffset + 26, true);
19260
+ const extraLength = view.getUint16(localOffset + 28, true);
19261
+ const start2 = localOffset + 30 + nameLength + extraLength;
19262
+ const body = archive.subarray(start2, start2 + compressedSize);
19263
+ if (method === 0) return body.slice();
19264
+ if (method !== 8) throw new ZipError(`${name}: unsupported compression method ${method}`);
19265
+ const inflated = inflateRaw(body);
19266
+ if (inflated.length !== uncompressedSize) {
19267
+ throw new ZipError(`${name}: inflated to ${inflated.length} bytes, the directory says ${uncompressedSize}`);
19268
+ }
19269
+ return inflated;
19270
+ }
19271
+ function findEndOfCentralDirectory(archive, view) {
19272
+ const at = findSignature(archive, END_OF_CENTRAL_DIRECTORY, archive.length);
19273
+ if (at < 0) throw new ZipError("not a zip archive: no end-of-central-directory record");
19274
+ return at;
19275
+ }
19276
+ function findSignature(archive, signature, before) {
19277
+ const view = new DataView(archive.buffer, archive.byteOffset, archive.byteLength);
19278
+ for (let at = Math.min(before, archive.length) - 4; at >= 0; at -= 1) {
19279
+ if (view.getUint32(at, true) === signature) return at;
19280
+ }
19281
+ return -1;
19282
+ }
19283
+
19284
+ // src/runtime/python/pypi.ts
19285
+ var ACCEPTED_TAGS = ["py3-none-any", "py2.py3-none-any", "py3-none-emscripten"];
19286
+ function parseRequirement(text2) {
19287
+ const cleaned = text2.replace(/#.*$/, "").trim();
19288
+ if (!cleaned) return null;
19289
+ const [head2, marker = null] = splitOnce(cleaned, ";");
19290
+ const match2 = /^([A-Za-z0-9._-]+)\s*(\[[^\]]*\])?\s*(.*)$/.exec(head2.trim());
19291
+ if (!match2) return null;
19292
+ const [, name, extrasGroup = "", rest = ""] = match2;
19293
+ const specifiers = [];
19294
+ for (const part of rest.split(",")) {
19295
+ const spec = /^\s*(===|==|>=|<=|!=|~=|>|<)\s*([A-Za-z0-9.*+!-]+)\s*$/.exec(part);
19296
+ if (spec) specifiers.push({ op: spec[1], version: spec[2] });
19297
+ }
19298
+ return {
19299
+ name: normalize2(name),
19300
+ specifiers,
19301
+ extras: extrasGroup.slice(1, -1).split(",").map((e) => e.trim()).filter(Boolean),
19302
+ marker: marker?.trim() ?? null
19303
+ };
19304
+ }
19305
+ function normalize2(name) {
19306
+ return name.toLowerCase().replace(/[-_.]+/g, "-");
19307
+ }
19308
+ function markerApplies(marker, environment, extras) {
19309
+ if (!marker) return true;
19310
+ const evaluateClause = (clause) => {
19311
+ const match2 = /^\s*([A-Za-z_]+)\s*(===|==|!=|>=|<=|>|<|not in|in)\s*(.+?)\s*$/.exec(clause);
19312
+ if (!match2) return false;
19313
+ const [, variable, op, rawValue] = match2;
19314
+ const value = rawValue.replace(/^['"]|['"]$/g, "");
19315
+ if (variable === "extra") {
19316
+ const wanted = extras.includes(value);
19317
+ return op === "==" ? wanted : op === "!=" ? !wanted : false;
19318
+ }
19319
+ const actual = environment[variable];
19320
+ if (actual === void 0) return false;
19321
+ if (op === "in") return value.includes(actual);
19322
+ if (op === "not in") return !value.includes(actual);
19323
+ if (variable === "python_version" || variable === "python_full_version") {
19324
+ return compareVersions(actual, value, op);
19325
+ }
19326
+ return op === "==" || op === "===" ? actual === value : op === "!=" ? actual !== value : false;
19327
+ };
19328
+ if (marker.includes("(")) return false;
19329
+ return marker.split(/\s+or\s+/).some((group) => group.split(/\s+and\s+/).every(evaluateClause));
19330
+ }
19331
+ function compareVersions(left, right, op) {
19332
+ if (op === "==" || op === "===") {
19333
+ if (right.endsWith(".*")) return left.startsWith(right.slice(0, -1));
19334
+ return releaseOf(left).join(".") === releaseOf(right).join(".");
19335
+ }
19336
+ if (op === "!=") return !compareVersions(left, right, "==");
19337
+ const order = compareRelease(releaseOf(left), releaseOf(right));
19338
+ if (op === ">=") return order >= 0;
19339
+ if (op === "<=") return order <= 0;
19340
+ if (op === ">") return order > 0;
19341
+ if (op === "<") return order < 0;
19342
+ if (op === "~=") {
19343
+ const bound = releaseOf(right).slice(0, -1);
19344
+ return order >= 0 && compareRelease(releaseOf(left).slice(0, bound.length), bound) === 0;
19345
+ }
19346
+ return false;
19347
+ }
19348
+ function releaseOf(version) {
19349
+ const release = /^\s*v?(\d+(?:\.\d+)*)/.exec(version.trim());
19350
+ return release ? release[1].split(".").map(Number) : [0];
19351
+ }
19352
+ function compareRelease(left, right) {
19353
+ for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
19354
+ const difference = (left[i] ?? 0) - (right[i] ?? 0);
19355
+ if (difference !== 0) return difference > 0 ? 1 : -1;
19356
+ }
19357
+ return 0;
19358
+ }
19359
+ function isPreRelease(version) {
19360
+ return /(a|b|rc|dev|post)\d*$/i.test(version.replace(/^\d+(\.\d+)*/, ""));
19361
+ }
19362
+ async function resolvePackage(client, requirement, environment) {
19363
+ const index = await client.json(`https://pypi.org/pypi/${requirement.name}/json`);
19364
+ 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)));
19365
+ const stable = candidates.filter((version) => !isPreRelease(version));
19366
+ const ordered = (stable.length > 0 ? stable : candidates).reverse();
19367
+ let sawSourceOnly = false;
19368
+ for (const version of ordered) {
19369
+ const files = (index.releases[version] ?? []).filter((file3) => !file3.yanked);
19370
+ const wheel = pickWheel(files);
19371
+ if (wheel) {
19372
+ return {
19373
+ name: requirement.name,
19374
+ version,
19375
+ url: wheel.url,
19376
+ filename: wheel.filename,
19377
+ sha256: wheel.digests?.sha256 ?? "",
19378
+ requires: []
19379
+ };
19380
+ }
19381
+ if (files.some((file3) => file3.packagetype === "sdist")) sawSourceOnly = true;
19382
+ }
19383
+ if (ordered.length === 0) {
19384
+ throw new Error(
19385
+ `no version of ${requirement.name} matches ${describe(requirement)}`
19386
+ );
19387
+ }
19388
+ throw new Error(
19389
+ 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 ")
19390
+ );
19391
+ }
19392
+ function pickWheel(files) {
19393
+ const wheels = files.filter((file3) => file3.packagetype === "bdist_wheel");
19394
+ for (const tag2 of ACCEPTED_TAGS) {
19395
+ const match2 = wheels.find((file3) => file3.filename.endsWith(`-${tag2}.whl`));
19396
+ if (match2) return match2;
19397
+ }
19398
+ return null;
19399
+ }
19400
+ function describe(requirement) {
19401
+ return requirement.specifiers.length === 0 ? "any version" : requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ");
19402
+ }
19403
+ function splitOnce(text2, separator) {
19404
+ const at = text2.indexOf(separator);
19405
+ return at < 0 ? [text2] : [text2.slice(0, at), text2.slice(at + 1)];
19406
+ }
19407
+
19408
+ // src/runtime/python/install.ts
19409
+ var SITE_PACKAGES = "/usr/lib/python3.13/site-packages";
19410
+ function markerEnvironment(pythonVersion) {
19411
+ const [major = "3", minor = "13"] = pythonVersion.split(".");
19412
+ return {
19413
+ python_version: `${major}.${minor}`,
19414
+ python_full_version: pythonVersion,
19415
+ sys_platform: "emscripten",
19416
+ platform_system: "Emscripten",
19417
+ platform_machine: "wasm32",
19418
+ implementation_name: "cpython",
19419
+ os_name: "posix"
19420
+ };
19421
+ }
19422
+ async function installRequirements(options) {
19423
+ const environment = markerEnvironment(options.pythonVersion);
19424
+ const report = { installed: [], skipped: [] };
19425
+ const queue = [];
19426
+ for (const text2 of options.requirements) {
19427
+ const requirement = parseRequirement(text2);
19428
+ if (requirement) queue.push({ requirement, extras: requirement.extras });
19429
+ }
19430
+ const chosen = /* @__PURE__ */ new Map();
19431
+ const seen = /* @__PURE__ */ new Set();
19432
+ while (queue.length > 0) {
19433
+ const { requirement, extras } = queue.shift();
19434
+ if (!markerApplies(requirement.marker, environment, extras)) {
19435
+ report.skipped.push({ name: requirement.name, marker: requirement.marker ?? "" });
19436
+ continue;
19437
+ }
19438
+ const key = `${requirement.name}[${extras.join(",")}]`;
19439
+ if (seen.has(key)) continue;
19440
+ seen.add(key);
19441
+ const already = chosen.get(requirement.name);
19442
+ if (already) {
19443
+ const satisfied = requirement.specifiers.every(
19444
+ (s) => compareVersions(already.version, s.version, s.op)
19445
+ );
19446
+ if (!satisfied) {
19447
+ throw new Error(
19448
+ `${requirement.name} ${already.version} is already selected but another dependency needs ` + requirement.specifiers.map((s) => `${s.op}${s.version}`).join(", ")
19449
+ );
19450
+ }
19451
+ continue;
19452
+ }
19453
+ options.progress.collecting(requirement.name);
19454
+ const resolved = await resolvePackage(options.client, requirement);
19455
+ chosen.set(requirement.name, resolved);
19456
+ options.progress.downloading(resolved.name, resolved.version);
19457
+ const archive = await options.client.bytes(resolved.url);
19458
+ const entries = readZip(archive);
19459
+ const metadata = entries.find((entry) => /\.dist-info\/METADATA$/.test(entry.name));
19460
+ if (metadata) {
19461
+ for (const line of new TextDecoder().decode(metadata.data()).split(/\r?\n/)) {
19462
+ if (!line.startsWith("Requires-Dist:")) continue;
19463
+ const dependency = parseRequirement(line.slice("Requires-Dist:".length));
19464
+ if (dependency) queue.push({ requirement: dependency, extras: [] });
19465
+ }
19466
+ }
19467
+ unpack(options.vfs, options.cred, entries);
19468
+ report.installed.push({ name: resolved.name, version: resolved.version });
19469
+ }
19470
+ return report;
19471
+ }
19472
+ function unpack(vfs, cred, entries, resolved) {
19473
+ const staged = [];
19474
+ for (const entry of entries) {
19475
+ if (entry.isDirectory) continue;
19476
+ if (/^[^/]+\.data\//.test(entry.name)) continue;
19477
+ staged.push({ path: `${SITE_PACKAGES}/${entry.name}`, data: entry.data() });
19478
+ }
19479
+ const directories = /* @__PURE__ */ new Set();
19480
+ for (const file3 of staged) directories.add(file3.path.slice(0, file3.path.lastIndexOf("/")));
19481
+ for (const directory2 of directories) vfs.mkdir(directory2, { recursive: true, cred });
19482
+ for (const file3 of staged) vfs.writeFile(file3.path, file3.data, { cred });
19483
+ }
19484
+
19485
+ // src/runtime/python/backend.ts
19486
+ function withSitePackages(env2) {
19487
+ const existing = env2.PYTHONPATH;
19488
+ return {
19489
+ ...env2,
19490
+ PYTHONPATH: existing ? `${SITE_PACKAGES}:${existing}` : SITE_PACKAGES
19491
+ };
19492
+ }
19493
+ var INTERPRETER_OWNED = /* @__PURE__ */ new Set(["lib", "dev", "proc", "usr"]);
19494
+ async function resolveWorkerUrl(explicit) {
19495
+ if (explicit) {
19496
+ try {
19497
+ return new URL(explicit);
19498
+ } catch {
19499
+ return explicit;
19500
+ }
19501
+ }
19502
+ const beside = new URL("./python-worker.js", import.meta.url);
19503
+ if (beside.protocol !== "file:") return beside;
19504
+ const [{ existsSync }, { fileURLToPath: fileURLToPath2, pathToFileURL: pathToFileURL2 }] = await Promise.all([
19505
+ nodeBuiltin("fs"),
19506
+ nodeBuiltin("url")
19507
+ ]);
19508
+ if (existsSync(fileURLToPath2(beside))) return beside;
19509
+ const built = new URL("../../../dist/python-worker.js", import.meta.url);
19510
+ if (existsSync(fileURLToPath2(built))) return pathToFileURL2(fileURLToPath2(built));
19511
+ throw new Error(
19512
+ "the SandboxedJs Python process worker has not been built; run `npm run build` in the package, or pass configurePython({ workerUrl })."
19513
+ );
19514
+ }
19515
+ function usingOwnedPython() {
19516
+ return pythonBackend().backend === "sbx-cpython-wasm";
19517
+ }
19518
+ async function runOwnedPython(ctx, argv) {
19519
+ const { manifest, workerUrl } = pythonBackend();
19520
+ if (!manifest) throw new Error("no Python runtime manifest is configured");
19521
+ const mounts = containerMounts(ctx);
19522
+ const process2 = await startPythonProcess({
19523
+ manifest,
19524
+ workerUrl: await resolveWorkerUrl(workerUrl),
19525
+ vfs: ctx.vfs,
19526
+ cred: ctx.cred,
19527
+ pid: ctx.proc.pid,
19528
+ ppid: ctx.proc.ppid ?? 1,
19529
+ argv,
19530
+ env: withSitePackages(ctx.env),
19531
+ cwd: ctx.cwd,
19532
+ isTty: isTerminal(ctx),
19533
+ mounts,
19534
+ onStdout: (data) => ctx.stdout.write(data),
19535
+ onStderr: (data) => ctx.stderr.write(data),
19536
+ signal: ctx.signal
19537
+ });
19538
+ void pumpStdin(ctx, process2);
19539
+ return process2.wait();
19540
+ }
19541
+ async function pumpStdin(ctx, proc) {
19542
+ const stdin = ctx.stdin;
19543
+ try {
19544
+ for (; ; ) {
19545
+ const chunk = await stdin.read(65536);
19546
+ if (chunk === null || chunk.length === 0) break;
19547
+ proc.write(chunk);
19548
+ }
19549
+ } catch {
19550
+ }
19551
+ proc.endInput();
19552
+ }
19553
+ function isTerminal(ctx) {
19554
+ const stdin = ctx.stdin;
19555
+ return Boolean(stdin.isTTY || stdin.interactive);
19556
+ }
19557
+ function containerMounts(ctx) {
19558
+ const mounts = [];
19559
+ let entries;
19560
+ try {
19561
+ entries = ctx.vfs.readdir("/", ctx.cred);
19562
+ } catch {
19563
+ return mounts;
19564
+ }
19565
+ for (const name of entries) {
19566
+ if (INTERPRETER_OWNED.has(name)) continue;
19567
+ try {
19568
+ if (!ctx.vfs.stat(`/${name}`, { cred: ctx.cred }).isDirectory()) continue;
19569
+ } catch {
19570
+ continue;
19571
+ }
19572
+ mounts.push({ guest: `/${name}`, container: `/${name}` });
19573
+ }
19574
+ try {
19575
+ if (ctx.vfs.stat(SITE_PACKAGES, { cred: ctx.cred }).isDirectory()) {
19576
+ mounts.push({ guest: SITE_PACKAGES, container: SITE_PACKAGES });
19577
+ }
19578
+ } catch {
19579
+ }
19580
+ return mounts;
19581
+ }
19582
+
19583
+ // src/runtime/python/pip-command.ts
19584
+ var pipCommand = defineCommand({
19585
+ name: "pip",
19586
+ path: "/usr/bin/pip",
19587
+ aliases: ["pip3"],
19588
+ summary: "install Python wheels into the container's site-packages",
19589
+ usage: "pip install [-r requirements.txt] <package>...",
19590
+ manual: `Resolves wheels from PyPI and unpacks them into ${SITE_PACKAGES}, which
19591
+ the interpreter has on sys.path.
19592
+
19593
+ This is not pip. It installs published wheels only: there are no source builds,
19594
+ no editable installs, no uninstall, and one site-packages rather than virtual
19595
+ environments. A package with no usable wheel is an error, never a silent skip.`,
19596
+ async run(ctx) {
19597
+ const [action, ...args] = ctx.args;
19598
+ if (action === "--version" || action === "-V") {
19599
+ ctx.line(`pip (sandboxedjs wheel installer, not pip) targeting ${SITE_PACKAGES}`);
19600
+ return 0;
19601
+ }
19602
+ if (action === "list") {
19603
+ return listInstalled(ctx);
19604
+ }
19605
+ if (action !== "install") {
19606
+ ctx.line("usage: pip install [-r requirements.txt] <package>...");
19607
+ return action === void 0 ? 1 : 0;
19608
+ }
19609
+ const requirements = [];
19610
+ for (let i = 0; i < args.length; i += 1) {
19611
+ const arg = args[i];
19612
+ if (arg === "-r" || arg === "--requirement") {
19613
+ const file3 = args[++i];
19614
+ if (!file3) return ctx.fail("option -r requires a file", 2);
19615
+ try {
19616
+ requirements.push(
19617
+ ...ctx.vfs.readText(ctx.path(file3), ctx.cred).split(/\r?\n/).map((line) => line.replace(/#.*$/, "").trim()).filter(Boolean)
19618
+ );
19619
+ } catch {
19620
+ return ctx.fail(`could not open requirements file '${file3}'`);
19621
+ }
19622
+ continue;
19623
+ }
19624
+ if (arg.startsWith("-")) continue;
19625
+ requirements.push(arg);
19626
+ }
19627
+ if (requirements.length === 0) return ctx.fail("no packages specified");
19628
+ if (!ctx.kernel.net.outboundAllowed("https://pypi.org")) {
19629
+ return ctx.fail(
19630
+ "outbound network access is disabled for this container (enable with network: { allowOutbound: true })"
19631
+ );
19632
+ }
19633
+ const client = {
19634
+ async json(url) {
19635
+ const response = await fetch(url);
19636
+ if (!response.ok) throw new Error(`${url} answered ${response.status}`);
19637
+ return response.json();
19638
+ },
19639
+ async bytes(url) {
19640
+ const response = await fetch(url);
19641
+ if (!response.ok) throw new Error(`${url} answered ${response.status}`);
19642
+ return new Uint8Array(await response.arrayBuffer());
19643
+ }
19644
+ };
19645
+ try {
19646
+ const report = await installRequirements({
19647
+ client,
19648
+ vfs: ctx.vfs,
19649
+ cred: ctx.cred,
19650
+ requirements,
19651
+ pythonVersion: pythonBackend().manifest?.pythonVersion ?? "3.13.5",
19652
+ progress: {
19653
+ collecting: (name) => ctx.line(`Collecting ${name}`),
19654
+ downloading: () => {
19655
+ }
19656
+ }
19657
+ });
19658
+ for (const skipped of report.skipped) {
19659
+ ctx.line(` Skipping ${skipped.name} (marker: ${skipped.marker})`);
19660
+ }
19661
+ ctx.line(
19662
+ `Successfully installed ${report.installed.map((p) => `${p.name}-${p.version}`).join(" ")}`
19663
+ );
19664
+ return 0;
19665
+ } catch (error) {
19666
+ return ctx.fail(error.message ?? String(error));
19667
+ }
19668
+ }
19669
+ });
19670
+ function listInstalled(ctx) {
19671
+ let entries;
19672
+ try {
19673
+ entries = ctx.vfs.readdir(SITE_PACKAGES, ctx.cred);
19674
+ } catch {
19675
+ return 0;
19676
+ }
19677
+ for (const entry of entries) {
19678
+ const match2 = /^(.+)-(.+)\.dist-info$/.exec(entry);
19679
+ if (match2) ctx.line(`${match2[1]} ${match2[2]}`);
19680
+ }
19681
+ return 0;
19682
+ }
19683
+
19684
+ // src/runtime/emscripten-fs.ts
19685
+ init_errno();
19686
+ init_path();
19687
+ var EM_ERRNO = {
19688
+ EPERM: 63,
19689
+ ENOENT: 44,
19690
+ EIO: 29,
19691
+ EBADF: 8,
19692
+ EACCES: 2,
19693
+ EBUSY: 10,
19694
+ EEXIST: 20,
19695
+ EXDEV: 75,
19696
+ ENODEV: 43,
19697
+ ENOTDIR: 54,
19698
+ EISDIR: 31,
19699
+ EINVAL: 28,
19700
+ ENFILE: 41,
19701
+ EMFILE: 33,
19702
+ ENOSPC: 51,
19703
+ EROFS: 57,
19704
+ ENOTEMPTY: 55,
19705
+ ELOOP: 32,
19706
+ ENOSYS: 52,
19707
+ ENOTSUP: 138
19708
+ };
19709
+ function emErrno(code) {
19710
+ return EM_ERRNO[code] ?? EM_ERRNO.EIO;
19711
+ }
19712
+ function mountContainerFs(FS, opts) {
19713
+ const { vfs, cred } = opts;
19714
+ const throwErrno = (code) => {
19715
+ throw new FS.ErrnoError(emErrno(code));
19716
+ };
19717
+ const translate = (e) => {
19718
+ if (isSysError(e)) return throwErrno(e.code);
19719
+ throw new FS.ErrnoError(emErrno("EIO"));
19720
+ };
19721
+ const mountRoot = opts.mountAt ? normalize(opts.mountAt) : "/";
19722
+ const backend = {
19723
+ /** Absolute container path for a node. */
19724
+ realPath(node2) {
19725
+ const parts = [];
19726
+ let current = node2;
19727
+ while (current && current.parent !== current) {
19728
+ parts.unshift(current.name);
19729
+ current = current.parent;
19730
+ }
19731
+ const relative2 = parts.join("/");
19732
+ if (mountRoot === "/") return "/" + relative2;
19733
+ return relative2 ? join(mountRoot, relative2) : mountRoot;
19734
+ },
19735
+ mount(_mount) {
19736
+ return backend.createNode(null, "/", 16877 | 0, 0);
19737
+ },
19738
+ createNode(parent, name, mode, rdev) {
19739
+ const node2 = FS.createNode(parent, name, mode, rdev);
19740
+ node2.node_ops = nodeOps;
19741
+ node2.stream_ops = streamOps;
19742
+ return node2;
19743
+ }
19744
+ };
19745
+ const nodeOps = {
19746
+ getattr(node2) {
19747
+ const path = backend.realPath(node2);
19748
+ try {
19749
+ const st = vfs.lstat(path);
19750
+ return {
19751
+ dev: st.dev,
19752
+ ino: st.ino,
19753
+ mode: st.mode,
19754
+ nlink: st.nlink,
19755
+ uid: st.uid,
19756
+ gid: st.gid,
19757
+ rdev: st.rdev,
19758
+ size: st.size,
19759
+ atime: new Date(st.atimeMs),
19760
+ mtime: new Date(st.mtimeMs),
19761
+ ctime: new Date(st.ctimeMs),
19762
+ blksize: 4096,
19763
+ blocks: Math.ceil(st.size / 512)
19764
+ };
19765
+ } catch (e) {
19766
+ return translate(e);
19767
+ }
19768
+ },
19769
+ setattr(node2, attr) {
19770
+ const path = backend.realPath(node2);
19771
+ try {
19772
+ if (attr.mode !== void 0) {
19773
+ vfs.chmod(path, attr.mode & 4095, cred);
19774
+ node2.mode = attr.mode;
19775
+ }
19776
+ if (attr.timestamp !== void 0) vfs.utimes(path, attr.timestamp, attr.timestamp, cred);
19777
+ if (attr.size !== void 0) {
19778
+ vfs.truncate(path, attr.size, cred);
19779
+ opts.onWrite?.(path);
19780
+ }
19781
+ } catch (e) {
19782
+ translate(e);
19783
+ }
19784
+ },
19785
+ lookup(parent, name) {
19786
+ const path = join(backend.realPath(parent), name);
19787
+ let mode;
19788
+ try {
19789
+ mode = vfs.lstat(path).mode;
19790
+ } catch {
19791
+ throw new FS.ErrnoError(EM_ERRNO.ENOENT);
19792
+ }
19793
+ return backend.createNode(parent, name, mode, 0);
19794
+ },
19795
+ mknod(parent, name, mode, dev) {
19796
+ const path = join(backend.realPath(parent), name);
19797
+ try {
19798
+ if (FS.isDir(mode)) {
19799
+ vfs.mkdir(path, { cred, mode: mode & 4095 });
19800
+ } else if (FS.isFile(mode)) {
19801
+ vfs.writeFile(path, new Uint8Array(0), { cred, mode: mode & 4095 });
19802
+ } else {
19803
+ return throwErrno("ENOTSUP");
19804
+ }
19805
+ opts.onWrite?.(path);
19806
+ } catch (e) {
19807
+ translate(e);
19808
+ }
19809
+ return backend.createNode(parent, name, mode, dev);
19810
+ },
19811
+ rename(oldNode, newDir, newName) {
19812
+ const from = backend.realPath(oldNode);
19813
+ const to = join(backend.realPath(newDir), newName);
19814
+ try {
19815
+ vfs.rename(from, to, cred);
19816
+ opts.onWrite?.(to);
19817
+ } catch (e) {
19818
+ translate(e);
19819
+ }
19820
+ oldNode.name = newName;
19821
+ oldNode.parent = newDir;
19822
+ },
19823
+ unlink(parent, name) {
19824
+ const path = join(backend.realPath(parent), name);
19825
+ try {
19826
+ vfs.unlink(path, cred);
19827
+ opts.onWrite?.(path);
17815
19828
  } catch (e) {
17816
19829
  translate(e);
17817
19830
  }
@@ -18698,9 +20711,9 @@ async function resolveIndexUrl() {
18698
20711
  if (indexUrl) return indexUrl;
18699
20712
  if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
18700
20713
  try {
18701
- const { createRequire: createRequire2 } = await nodeBuiltin("module");
20714
+ const { createRequire } = await nodeBuiltin("module");
18702
20715
  const path = await nodeBuiltin("path");
18703
- const require2 = createRequire2(path.join(process.cwd(), "index.js"));
20716
+ const require2 = createRequire(path.join(process.cwd(), "index.js"));
18704
20717
  return `${path.dirname(require2.resolve("pyodide/package.json"))}/`;
18705
20718
  } catch {
18706
20719
  return void 0;
@@ -18980,6 +20993,12 @@ var micropip = defineCommand({
18980
20993
  summary: "install Python packages into the running interpreter",
18981
20994
  usage: "micropip install <package>...",
18982
20995
  async run(ctx) {
20996
+ if (usingOwnedPython()) {
20997
+ ctx.warn(
20998
+ "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)."
20999
+ );
21000
+ return 1;
21001
+ }
18983
21002
  const [action, ...args] = ctx.args;
18984
21003
  if (action === "--version" || action === "-V") {
18985
21004
  ctx.line("pip (micropip, Pyodide)");
@@ -19066,6 +21085,11 @@ var micropip = defineCommand({
19066
21085
  var PYTHON_VERSION = "3.13";
19067
21086
  function configurePython(options = {}) {
19068
21087
  configureCPython({ indexURL: options.indexURL, moduleURL: options.pyodideURL });
21088
+ setPythonBackend({
21089
+ ...options.backend !== void 0 ? { backend: options.backend } : {},
21090
+ ...options.manifest !== void 0 ? { manifest: options.manifest } : {},
21091
+ ...options.workerUrl !== void 0 ? { workerUrl: options.workerUrl } : {}
21092
+ });
19069
21093
  }
19070
21094
  var isPythonAvailable = isCPythonAvailable;
19071
21095
  var python = defineCommand({
@@ -19078,6 +21102,13 @@ var python = defineCommand({
19078
21102
  the container's virtual filesystem, modules in /workspace are importable, and
19079
21103
  compatible packages can be installed with pip (micropip).`,
19080
21104
  async run(ctx) {
21105
+ if (usingOwnedPython()) {
21106
+ try {
21107
+ return await runOwnedPython(ctx, ctx.args);
21108
+ } catch (error) {
21109
+ return ctx.fail(error.message ?? String(error));
21110
+ }
21111
+ }
19081
21112
  const argv = ctx.args;
19082
21113
  let i = 0;
19083
21114
  let command;
@@ -19158,22 +21189,22 @@ function printHelp2(ctx) {
19158
21189
  ctx.line("-V : print the Python version and exit");
19159
21190
  }
19160
21191
  function pythonCommands() {
19161
- return [python, micropip];
21192
+ return usingOwnedPython() ? [python, pipCommand] : [python, micropip];
19162
21193
  }
19163
21194
 
19164
21195
  // src/runtime/ffmpeg.ts
19165
21196
  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";
19166
21197
  var compiled = null;
19167
21198
  async function loadFactory() {
19168
- const { createRequire: createRequire2 } = await nodeBuiltin("module");
21199
+ const { createRequire } = await nodeBuiltin("module");
19169
21200
  const fs = await nodeBuiltin("fs/promises");
19170
21201
  const path = await nodeBuiltin("path");
19171
- const packageRequire = createRequire2(import.meta.url);
21202
+ const packageRequire = createRequire(import.meta.url);
19172
21203
  let require2 = packageRequire;
19173
21204
  try {
19174
21205
  require2.resolve("@ffmpeg/core/wasm");
19175
21206
  } catch {
19176
- require2 = createRequire2(path.join(process.cwd(), "index.js"));
21207
+ require2 = createRequire(path.join(process.cwd(), "index.js"));
19177
21208
  }
19178
21209
  const wasmPath = require2.resolve("@ffmpeg/core/wasm");
19179
21210
  const wasm = await fs.readFile(wasmPath);
@@ -21702,19 +23733,19 @@ var KernelChildProcess = class {
21702
23733
  process;
21703
23734
  started = false;
21704
23735
  cancelled = false;
21705
- constructor(kernel, cred, config, pid) {
23736
+ constructor(kernel, cred, config2, pid) {
21706
23737
  this.stdin.interactive = true;
21707
- if (config.inheritStdio) this.stdin.isTTY = true;
21708
- if (config.stdinIgnored) this.stdin.end();
23738
+ if (config2.inheritStdio) this.stdin.isTTY = true;
23739
+ if (config2.stdinIgnored) this.stdin.end();
21709
23740
  this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
21710
23741
  this.kernel = kernel;
21711
23742
  this.cred = cred;
21712
23743
  this.pid = pid;
21713
- this.command = config.command;
21714
- this.args = config.args?.slice() ?? [];
21715
- this.parentPid = config.parentPid;
21716
- this.cwd = config.cwd ?? "/";
21717
- this.env = { ...config.env };
23744
+ this.command = config2.command;
23745
+ this.args = config2.args?.slice() ?? [];
23746
+ this.parentPid = config2.parentPid;
23747
+ this.cwd = config2.cwd ?? "/";
23748
+ this.env = { ...config2.env };
21718
23749
  }
21719
23750
  on(event, listener) {
21720
23751
  let listeners = this.listeners.get(event);
@@ -21821,12 +23852,12 @@ var nextBridgePid = 1073741824;
21821
23852
  function installNodeChildProcessBridge(pod, kernel, cred) {
21822
23853
  const manager = pod.processManager;
21823
23854
  const originalSpawn = manager.spawn;
21824
- manager.spawn = (config) => {
21825
- const resolved = kernel.resolveExecutable(config.command, config.cwd ?? "/", config.env ?? {}, cred);
23855
+ manager.spawn = (config2) => {
23856
+ const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
21826
23857
  if (resolved?.kind === "builtin") {
21827
- return new KernelChildProcess(kernel, cred, config, nextBridgePid++);
23858
+ return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
21828
23859
  }
21829
- return originalSpawn.call(manager, config);
23860
+ return originalSpawn.call(manager, config2);
21830
23861
  };
21831
23862
  return () => {
21832
23863
  if (manager.spawn === originalSpawn) return;
@@ -23188,14 +25219,14 @@ var AssertionError = class extends Error {
23188
25219
  code = "ERR_ASSERTION";
23189
25220
  constructor(options) {
23190
25221
  const generated = options.message === void 0;
23191
- super(options.message ?? describe(options.actual, options.expected, options.operator));
25222
+ super(options.message ?? describe2(options.actual, options.expected, options.operator));
23192
25223
  this.actual = options.actual;
23193
25224
  this.expected = options.expected;
23194
25225
  this.operator = options.operator;
23195
25226
  this.generatedMessage = generated;
23196
25227
  }
23197
25228
  };
23198
- function describe(actual, expected, operator) {
25229
+ function describe2(actual, expected, operator) {
23199
25230
  if (operator === "fail") return "Failed";
23200
25231
  const rendered = (value) => inspect(value, { depth: 2 });
23201
25232
  return `${rendered(actual)} ${operator} ${rendered(expected)}`;
@@ -24033,9 +26064,9 @@ var VirtualClientRequest = class extends streamModule4.Writable {
24033
26064
  } catch (error) {
24034
26065
  clearTimeout(this.timer);
24035
26066
  if (this.destroyedByUser) return;
24036
- const failure = error instanceof Error ? error : new Error(String(error));
24037
- if (!("code" in failure)) Object.assign(failure, { code: "ECONNREFUSED" });
24038
- this.emit("error", failure);
26067
+ const failure2 = error instanceof Error ? error : new Error(String(error));
26068
+ if (!("code" in failure2)) Object.assign(failure2, { code: "ECONNREFUSED" });
26069
+ this.emit("error", failure2);
24039
26070
  } finally {
24040
26071
  if (!settled) untrack();
24041
26072
  }
@@ -24233,17 +26264,17 @@ function toBytes2(data, encoding) {
24233
26264
  }
24234
26265
 
24235
26266
  // src/runtime/sync-channel.ts
24236
- var STATE = 0;
24237
- var LENGTH = 1;
24238
- var MORE = 2;
24239
- var CONTROL_WORDS = 4;
24240
- var STATE_REQUEST = 1;
24241
- var STATE_RESPONSE = 2;
24242
- var STATE_CONTINUE = 3;
24243
- var STATE_CLOSED = 4;
26267
+ var STATE2 = 0;
26268
+ var LENGTH2 = 1;
26269
+ var MORE2 = 2;
26270
+ var CONTROL_WORDS2 = 4;
26271
+ var STATE_REQUEST2 = 1;
26272
+ var STATE_RESPONSE2 = 2;
26273
+ var STATE_CONTINUE2 = 3;
26274
+ var STATE_CLOSED2 = 4;
24244
26275
  function createSyncChannelBuffers(capacityBytes = 1 << 20) {
24245
26276
  return {
24246
- control: new SharedArrayBuffer(CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT),
26277
+ control: new SharedArrayBuffer(CONTROL_WORDS2 * Int32Array.BYTES_PER_ELEMENT),
24247
26278
  data: new SharedArrayBuffer(capacityBytes)
24248
26279
  };
24249
26280
  }
@@ -24277,15 +26308,15 @@ var SyncChannelServer = class {
24277
26308
  /** Call from the wake message the client sends after each chunk. */
24278
26309
  async pump() {
24279
26310
  if (this.closed) return;
24280
- const state = Atomics.load(this.control, STATE);
24281
- if (state === STATE_REQUEST) {
24282
- const size = Atomics.load(this.control, LENGTH);
26311
+ const state = Atomics.load(this.control, STATE2);
26312
+ if (state === STATE_REQUEST2) {
26313
+ const size = Atomics.load(this.control, LENGTH2);
24283
26314
  this.incoming.push(this.data.slice(0, size));
24284
- if (Atomics.load(this.control, MORE) === 1) {
24285
- this.publish(STATE_CONTINUE);
26315
+ if (Atomics.load(this.control, MORE2) === 1) {
26316
+ this.publish(STATE_CONTINUE2);
24286
26317
  return;
24287
26318
  }
24288
- const request = concat3(this.incoming);
26319
+ const request = concat4(this.incoming);
24289
26320
  this.incoming = [];
24290
26321
  let response;
24291
26322
  try {
@@ -24299,30 +26330,30 @@ var SyncChannelServer = class {
24299
26330
  this.sendChunk();
24300
26331
  return;
24301
26332
  }
24302
- if (state === STATE_CONTINUE) this.sendChunk();
26333
+ if (state === STATE_CONTINUE2) this.sendChunk();
24303
26334
  }
24304
26335
  /** Release a blocked client, e.g. when the container is torn down. */
24305
26336
  close() {
24306
26337
  if (this.closed) return;
24307
26338
  this.closed = true;
24308
- Atomics.store(this.control, STATE, STATE_CLOSED);
24309
- Atomics.notify(this.control, STATE);
26339
+ Atomics.store(this.control, STATE2, STATE_CLOSED2);
26340
+ Atomics.notify(this.control, STATE2);
24310
26341
  }
24311
26342
  sendChunk() {
24312
26343
  const payload = this.outgoing ?? new Uint8Array();
24313
26344
  const size = Math.min(this.capacity, payload.length - this.sent);
24314
26345
  this.data.set(payload.subarray(this.sent, this.sent + size), 0);
24315
26346
  this.sent += size;
24316
- Atomics.store(this.control, LENGTH, size);
24317
- Atomics.store(this.control, MORE, this.sent < payload.length ? 1 : 0);
24318
- this.publish(STATE_RESPONSE);
26347
+ Atomics.store(this.control, LENGTH2, size);
26348
+ Atomics.store(this.control, MORE2, this.sent < payload.length ? 1 : 0);
26349
+ this.publish(STATE_RESPONSE2);
24319
26350
  }
24320
26351
  publish(state) {
24321
- Atomics.store(this.control, STATE, state);
24322
- Atomics.notify(this.control, STATE);
26352
+ Atomics.store(this.control, STATE2, state);
26353
+ Atomics.notify(this.control, STATE2);
24323
26354
  }
24324
26355
  };
24325
- function concat3(parts) {
26356
+ function concat4(parts) {
24326
26357
  if (parts.length === 1) return parts[0];
24327
26358
  const total = parts.reduce((sum, part) => sum + part.length, 0);
24328
26359
  const joined = new Uint8Array(total);
@@ -24455,13 +26486,13 @@ ${err.join("")}`),
24455
26486
  return child;
24456
26487
  };
24457
26488
  const exec = (command, options = {}, callback) => {
24458
- const [opts, cb] = normalize2(options, callback);
26489
+ const [opts, cb] = normalize3(options, callback);
24459
26490
  const { file: file3, args } = throughShell(command, opts);
24460
26491
  return collect(start2(file3, args, { ...opts, shell: false }), opts, cb);
24461
26492
  };
24462
26493
  const execFile = (file3, args = [], options = {}, callback) => {
24463
26494
  const list = Array.isArray(args) ? args : [];
24464
- const [opts, cb] = Array.isArray(args) ? normalize2(options, callback) : normalize2(args, options);
26495
+ const [opts, cb] = Array.isArray(args) ? normalize3(options, callback) : normalize3(args, options);
24465
26496
  return collect(start2(file3, list, opts), opts, cb);
24466
26497
  };
24467
26498
  return {
@@ -24533,7 +26564,7 @@ ${result.stderr}`), {
24533
26564
  const execSync = (command, options = {}) => orThrow(run(command, [], { ...options, shell: options.shell ?? true }), command, options);
24534
26565
  return { spawnSync, execFileSync, execSync };
24535
26566
  }
24536
- function normalize2(options, callback) {
26567
+ function normalize3(options, callback) {
24537
26568
  if (typeof options === "function") return [{}, options];
24538
26569
  return [options ?? {}, callback];
24539
26570
  }
@@ -24925,7 +26956,7 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
24925
26956
  }
24926
26957
  }
24927
26958
  }
24928
- const isTerminal = (options, output) => {
26959
+ const isTerminal2 = (options, output) => {
24929
26960
  if (typeof options?.terminal === "boolean") return options.terminal;
24930
26961
  return Boolean(output?.isTTY);
24931
26962
  };
@@ -24935,13 +26966,13 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
24935
26966
  const instance = new Interface(
24936
26967
  options.input ?? defaultInput(),
24937
26968
  resolvedOutput2,
24938
- isTerminal(options, resolvedOutput2)
26969
+ isTerminal2(options, resolvedOutput2)
24939
26970
  );
24940
26971
  if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
24941
26972
  return instance;
24942
26973
  }
24943
26974
  const resolvedOutput = output;
24944
- return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal(void 0, resolvedOutput));
26975
+ return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal2(void 0, resolvedOutput));
24945
26976
  };
24946
26977
  const noop = () => {
24947
26978
  };
@@ -26908,44 +28939,69 @@ function ensureProcessGlobal() {
26908
28939
  configurable: true
26909
28940
  });
26910
28941
  }
28942
+
28943
+ // src/runtime/rolldown-node-binding.ts
28944
+ async function loadNodeApi() {
28945
+ const [moduleApi, fsApi, pathApi, urlApi] = await Promise.all([
28946
+ nodeBuiltin("module"),
28947
+ nodeBuiltin("fs"),
28948
+ nodeBuiltin("path"),
28949
+ nodeBuiltin("url")
28950
+ ]);
28951
+ return {
28952
+ createRequire: moduleApi.createRequire,
28953
+ existsSync: fsApi.existsSync,
28954
+ readFileSync: fsApi.readFileSync,
28955
+ dirname: pathApi.dirname,
28956
+ join: pathApi.join,
28957
+ fileURLToPath: urlApi.fileURLToPath,
28958
+ pathToFileURL: urlApi.pathToFileURL
28959
+ };
28960
+ }
26911
28961
  var BINDING_PACKAGE = "@rolldown/binding-wasm32-wasi";
26912
28962
  var ASYNC_WORK_POOL_SIZE = 4;
26913
28963
  var WORKER_FILE = "rolldown-wasi-worker.js";
26914
28964
  async function loadNodeRolldownMemfsBinding() {
28965
+ const node2 = await loadNodeApi();
28966
+ const { createRequire, existsSync, readFileSync, dirname: dirname3, join: join3, fileURLToPath: fileURLToPath2, pathToFileURL: pathToFileURL2 } = node2;
26915
28967
  const require2 = createRequire(import.meta.url);
26916
28968
  let packageDir;
26917
28969
  try {
26918
- packageDir = dirname$1(require2.resolve(`${BINDING_PACKAGE}/package.json`));
28970
+ packageDir = dirname3(require2.resolve(`${BINDING_PACKAGE}/package.json`));
26919
28971
  } catch {
26920
28972
  return null;
26921
28973
  }
26922
- const fromBinding = createRequire(join$1(packageDir, "package.json"));
28974
+ const fromBinding = createRequire(join3(packageDir, "package.json"));
26923
28975
  let runtime;
26924
28976
  let fsModuleUrl;
26925
28977
  let createContext2;
26926
28978
  try {
26927
28979
  runtime = fromBinding("@napi-rs/wasm-runtime");
26928
- fsModuleUrl = resolveSubpath(fromBinding, "@napi-rs/wasm-runtime", "./fs");
28980
+ fsModuleUrl = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", "./fs");
26929
28981
  ({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
26930
28982
  } catch {
26931
28983
  return null;
26932
28984
  }
26933
- const { memfs } = await import(fsModuleUrl);
26934
- const { Worker: Worker2 } = await import('worker_threads');
28985
+ const { memfs } = await import(
28986
+ /* @vite-ignore */
28987
+ /* webpackIgnore: true */
28988
+ fsModuleUrl
28989
+ );
28990
+ const { Worker: Worker2 } = await nodeBuiltin("worker_threads");
26935
28991
  const { fs: memoryFs, vol } = memfs();
26936
28992
  const wasi2 = new runtime.WASI({
26937
28993
  version: "preview1",
26938
28994
  fs: memoryFs,
26939
28995
  preopens: { "/": "/" }
26940
28996
  });
26941
- const wasmFile = readFileSync(join$1(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
28997
+ const wasmFile = readFileSync(join3(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
26942
28998
  const sharedMemory = new WebAssembly.Memory({ initial: 16384, maximum: 65536, shared: true });
26943
28999
  const workerPoolSize = Math.max(2, cpuCount());
26944
29000
  const context = createContext2({ autoDestroy: false });
26945
29001
  context.suppressDestroy();
26946
- const workerPath = resolveWorkerPath();
29002
+ const workerPath = resolveWorkerPath(node2);
26947
29003
  if (!workerPath) return null;
26948
- const runtimeAnchor = pathToFileURL$1(join$1(packageDir, "package.json")).href;
29004
+ const runtimeAnchor = pathToFileURL2(join3(packageDir, "package.json")).href;
26949
29005
  const workers = /* @__PURE__ */ new Set();
26950
29006
  let started = false;
26951
29007
  const { napiModule } = await runtime.instantiateNapiModule(wasmFile, {
@@ -26997,24 +29053,26 @@ async function loadNodeRolldownMemfsBinding() {
26997
29053
  for (const worker of workers) unreferenceWorker(worker);
26998
29054
  return { ...napiModule.exports, __fs: memoryFs, __volume: vol };
26999
29055
  }
27000
- function resolveWorkerPath() {
27001
- const here = dirname$1(fileURLToPath$1(import.meta.url));
29056
+ function resolveWorkerPath(node2) {
29057
+ const { dirname: dirname3, existsSync, fileURLToPath: fileURLToPath2, join: join3 } = node2;
29058
+ const here = dirname3(fileURLToPath2(import.meta.url));
27002
29059
  const candidates = [
27003
- join$1(here, WORKER_FILE),
29060
+ join3(here, WORKER_FILE),
27004
29061
  /* src/runtime → dist, for a checkout that has been built. */
27005
- join$1(here, "..", "..", "dist", WORKER_FILE)
29062
+ join3(here, "..", "..", "dist", WORKER_FILE)
27006
29063
  ];
27007
29064
  return candidates.find((candidate) => existsSync(candidate)) ?? null;
27008
29065
  }
27009
- function resolveSubpath(require2, packageName, subpath) {
27010
- const packageDir = dirname$1(require2.resolve(packageName));
27011
- const manifest = JSON.parse(readFileSync(join$1(packageDir, "package.json"), "utf8"));
29066
+ function resolveSubpath(node2, require2, packageName, subpath) {
29067
+ const { dirname: dirname3, join: join3, pathToFileURL: pathToFileURL2, readFileSync } = node2;
29068
+ const packageDir = dirname3(require2.resolve(packageName));
29069
+ const manifest = JSON.parse(readFileSync(join3(packageDir, "package.json"), "utf8"));
27012
29070
  const entry = manifest?.exports?.[subpath];
27013
29071
  const target = typeof entry === "string" ? entry : entry?.import ?? entry?.default;
27014
29072
  if (typeof target !== "string") {
27015
29073
  throw new Error(`${packageName} does not export ${subpath}`);
27016
29074
  }
27017
- return pathToFileURL$1(join$1(packageDir, target)).href;
29075
+ return pathToFileURL2(join3(packageDir, target)).href;
27018
29076
  }
27019
29077
  function cpuCount() {
27020
29078
  try {
@@ -27193,9 +29251,9 @@ var LocalProcess = class extends EventEmitter4 {
27193
29251
  }
27194
29252
  };
27195
29253
  var PodChildProcess = class {
27196
- constructor(pid, config, launch) {
29254
+ constructor(pid, config2, launch) {
27197
29255
  this.pid = pid;
27198
- this.config = config;
29256
+ this.config = config2;
27199
29257
  this.launch = launch;
27200
29258
  }
27201
29259
  pid;
@@ -27333,15 +29391,54 @@ var LocalRuntimePod = class _LocalRuntimePod {
27333
29391
  instanceId = `sbx-${Math.random().toString(36).slice(2)}`;
27334
29392
  router = new VirtualHttpRouter();
27335
29393
  proxy = { activePorts: (_instanceId) => this.router.activePorts() };
29394
+ /**
29395
+ * Bind a port to a server that is not a JavaScript one.
29396
+ *
29397
+ * The request is flattened to bytes and headers before it crosses the
29398
+ * boundary, because the far side is a different language and a Node stream
29399
+ * object cannot go there. What comes back is a complete response; streaming
29400
+ * is a later protocol, and pretending to stream over a buffered path would
29401
+ * only move the surprise.
29402
+ */
29403
+ serveExternal(port, owner, handler) {
29404
+ const server = new VirtualHttpServer(this.router, owner);
29405
+ server.on("request", (request, response) => {
29406
+ void (async () => {
29407
+ const chunks = [];
29408
+ for await (const chunk of request) chunks.push(chunk);
29409
+ const body = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
29410
+ let at = 0;
29411
+ for (const chunk of chunks) {
29412
+ body.set(chunk, at);
29413
+ at += chunk.length;
29414
+ }
29415
+ try {
29416
+ const answer = await handler({
29417
+ method: request.method,
29418
+ path: request.url,
29419
+ headers: request.headers,
29420
+ body
29421
+ });
29422
+ response.writeHead(answer.statusCode, answer.headers ?? {});
29423
+ response.end(answer.body ?? "");
29424
+ } catch (error) {
29425
+ response.writeHead(500, { "content-type": "text/plain" });
29426
+ response.end(error instanceof Error ? error.message : String(error));
29427
+ }
29428
+ })();
29429
+ });
29430
+ server.listen(port);
29431
+ return () => server.close();
29432
+ }
27336
29433
  /**
27337
29434
  * Mutable on purpose: a container replaces `spawn` so that children resolve
27338
29435
  * against the kernel's PATH. Left alone, it runs `node` and reports anything
27339
29436
  * else as not found, which is the correct answer for a bare pod.
27340
29437
  */
27341
29438
  processManager = {
27342
- spawn: (config) => new PodChildProcess(
29439
+ spawn: (config2) => new PodChildProcess(
27343
29440
  nextChildPid++,
27344
- config,
29441
+ config2,
27345
29442
  (child) => this.spawn(child.command, child.args ?? [], {
27346
29443
  ...child.cwd ? { cwd: child.cwd } : {},
27347
29444
  ...child.env ? { env: child.env } : {}
@@ -27414,7 +29511,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
27414
29511
  if (engine?.isEvaluating) throw new ProcessExit(code);
27415
29512
  },
27416
29513
  http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
27417
- spawnChild: (config) => this.processManager.spawn(config),
29514
+ spawnChild: (config2) => this.processManager.spawn(config2),
27418
29515
  ...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
27419
29516
  ...options.interactiveStdin ? { interactiveStdin: true } : {},
27420
29517
  ...options.tty ? { tty: true } : {},
@@ -27635,10 +29732,10 @@ function serveVolume(volume) {
27635
29732
  }
27636
29733
  return encodeFrame({ ok: true, value: result });
27637
29734
  } catch (error) {
27638
- const failure = error;
29735
+ const failure2 = error;
27639
29736
  return encodeFrame({
27640
29737
  ok: false,
27641
- error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
29738
+ error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
27642
29739
  });
27643
29740
  }
27644
29741
  };
@@ -27655,7 +29752,7 @@ function serveSyncSyscalls(options) {
27655
29752
  const result = await options.spawnChild(header.args[0]);
27656
29753
  return encodeFrame({ ok: true, value: result });
27657
29754
  } catch (error) {
27658
- const failure = error;
29755
+ const failure2 = error;
27659
29756
  return encodeFrame({
27660
29757
  ok: true,
27661
29758
  value: {
@@ -27663,81 +29760,10 @@ function serveSyncSyscalls(options) {
27663
29760
  stdout: "",
27664
29761
  stderr: "",
27665
29762
  signal: null,
27666
- error: { ...failure.code ? { code: failure.code } : {}, message: failure.message ?? String(error) }
27667
- }
27668
- });
27669
- }
27670
- };
27671
- }
27672
-
27673
- // src/runtime/worker-host.ts
27674
- function defaultWorkerUrl() {
27675
- return new URL("./worker-entry.js", import.meta.url);
27676
- }
27677
- function hasDomWorker() {
27678
- return typeof Worker === "function";
27679
- }
27680
- async function startRuntimeWorker(options = {}) {
27681
- const url = options.url ?? defaultWorkerUrl();
27682
- const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
27683
- try {
27684
- await new Promise((resolve2, reject) => {
27685
- const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
27686
- worker.onMessage((message) => {
27687
- if (message?.type === "sandboxedjs:ready") {
27688
- clearTimeout(timer);
27689
- resolve2();
29763
+ error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
27690
29764
  }
27691
29765
  });
27692
- worker.onError((error) => {
27693
- clearTimeout(timer);
27694
- reject(error instanceof Error ? error : new Error(String(error)));
27695
- });
27696
- });
27697
- } catch (error) {
27698
- try {
27699
- await worker.terminate();
27700
- } catch {
27701
29766
  }
27702
- throw error;
27703
- }
27704
- return worker;
27705
- }
27706
- async function startDomWorker(url) {
27707
- const worker = new Worker(url, { type: "module" });
27708
- const listeners = [];
27709
- const errors = [];
27710
- worker.addEventListener("message", (event) => {
27711
- for (const listener of listeners) listener(event.data);
27712
- });
27713
- worker.addEventListener("error", (event) => {
27714
- const detail = event.message || "the runtime worker failed to load";
27715
- for (const listener of errors) listener(new Error(detail));
27716
- });
27717
- return {
27718
- postMessage: (message) => worker.postMessage(message),
27719
- onMessage: (listener) => {
27720
- listeners.push(listener);
27721
- },
27722
- onError: (listener) => {
27723
- errors.push(listener);
27724
- },
27725
- terminate: () => worker.terminate()
27726
- };
27727
- }
27728
- async function startNodeWorker(url, workerData) {
27729
- const specifier = ["node", "worker_threads"].join(":");
27730
- const { Worker: NodeWorker } = await import(
27731
- /* @vite-ignore */
27732
- /* webpackIgnore: true */
27733
- specifier
27734
- );
27735
- const worker = new NodeWorker(url, { workerData });
27736
- return {
27737
- postMessage: (message) => worker.postMessage(message),
27738
- onMessage: (listener) => worker.on("message", listener),
27739
- onError: (listener) => worker.on("error", listener),
27740
- terminate: () => worker.terminate()
27741
29767
  };
27742
29768
  }
27743
29769
  var WorkerProcess = class extends EventEmitter4 {
@@ -28001,8 +30027,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
28001
30027
  ...request.inheritStdio ? { inheritStdio: true } : {}
28002
30028
  });
28003
30029
  } catch (error) {
28004
- const failure = error;
28005
- resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure.code ? { code: failure.code } : {}, message: failure.message } });
30030
+ const failure2 = error;
30031
+ resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message } });
28006
30032
  return;
28007
30033
  }
28008
30034
  owned.add(handle);
@@ -28057,7 +30083,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
28057
30083
  async forward(worker, port, request, response) {
28058
30084
  const chunks = [];
28059
30085
  for await (const chunk of request) chunks.push(chunk);
28060
- const body = concat4(chunks);
30086
+ const body = concat5(chunks);
28061
30087
  const id = this.nextRequestId++;
28062
30088
  const answered = new Promise((resolve2) => this.waiting.set(id, resolve2));
28063
30089
  worker.postMessage({
@@ -28138,7 +30164,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
28138
30164
  function isRecord2(value) {
28139
30165
  return typeof value === "object" && value !== null && !Array.isArray(value);
28140
30166
  }
28141
- function concat4(parts) {
30167
+ function concat5(parts) {
28142
30168
  if (parts.length === 0) return new Uint8Array();
28143
30169
  if (parts.length === 1) return parts[0];
28144
30170
  const total = parts.reduce((sum, part) => sum + part.length, 0);
@@ -29275,6 +31301,6 @@ async function renderInto(box, element, options = { port: 80 }) {
29275
31301
  // src/index.ts
29276
31302
  var src_default = createContainer;
29277
31303
 
29278
- export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WasiError, WasiExit, WasiHost, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, containerTarget, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, isWasmBinary, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, runWasi, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo, wasi as wasiCommand };
31304
+ export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MANIFEST_FORMAT, MANIFEST_SCHEMA_VERSION, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WasiError, WasiExit, WasiHost, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, containerTarget, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, isWasmBinary, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, runWasi, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo, validateManifest, wasi as wasiCommand };
29279
31305
  //# sourceMappingURL=index.js.map
29280
31306
  //# sourceMappingURL=index.js.map