sandboxedjs 0.1.47 → 0.1.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.cts +2 -1
- package/dist/agent.d.ts +2 -1
- package/dist/{container-DyRF-bY0.d.cts → container-DBMPLbUu.d.ts} +103 -410
- package/dist/{container-DyRF-bY0.d.ts → container-Dg8NwBc7.d.cts} +103 -410
- package/dist/index.cjs +2483 -311
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -65
- package/dist/index.d.ts +52 -65
- package/dist/index.js +2479 -310
- package/dist/index.js.map +1 -1
- package/dist/memory-volume-CTOXNJ6n.d.ts +66 -0
- package/dist/memory-volume-DCzGZFJb.d.cts +66 -0
- package/dist/python/python.data +16026 -66
- package/dist/python/python.js +2 -0
- package/dist/python/python.wasm +0 -0
- package/dist/python/python.worker.js +87 -0
- package/dist/python/runtime.json +34 -0
- package/dist/python-abi.cjs +2892 -0
- package/dist/python-abi.cjs.map +1 -0
- package/dist/python-abi.d.cts +645 -0
- package/dist/python-abi.d.ts +645 -0
- package/dist/python-abi.js +2839 -0
- package/dist/python-abi.js.map +1 -0
- package/dist/python-worker.js +865 -0
- package/dist/python-worker.js.map +1 -0
- package/dist/rolldown-wasi-worker.js +5 -1
- package/dist/rolldown-wasi-worker.js.map +1 -1
- package/dist/service-worker.js +34 -5
- package/dist/service-worker.js.map +1 -1
- package/dist/vfs-DzEcPbMY.d.cts +427 -0
- package/dist/vfs-DzEcPbMY.d.ts +427 -0
- package/package.json +8 -3
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
|
|
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 (
|
|
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
|
|
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 =
|
|
13921
|
-
const y =
|
|
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
|
|
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(
|
|
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/
|
|
17672
|
-
|
|
17673
|
-
|
|
17674
|
-
var
|
|
17675
|
-
|
|
17676
|
-
|
|
17677
|
-
|
|
17678
|
-
|
|
17679
|
-
|
|
17680
|
-
|
|
17681
|
-
|
|
17682
|
-
|
|
17683
|
-
|
|
17684
|
-
|
|
17685
|
-
|
|
17686
|
-
|
|
17687
|
-
|
|
17688
|
-
|
|
17689
|
-
|
|
17690
|
-
|
|
17691
|
-
|
|
17692
|
-
|
|
17693
|
-
|
|
17694
|
-
|
|
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
|
-
|
|
17697
|
-
|
|
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
|
-
|
|
17700
|
-
|
|
17701
|
-
|
|
17702
|
-
|
|
17703
|
-
|
|
17704
|
-
|
|
17705
|
-
|
|
17706
|
-
|
|
17707
|
-
|
|
17708
|
-
|
|
17709
|
-
|
|
17710
|
-
|
|
17711
|
-
|
|
17712
|
-
|
|
17713
|
-
|
|
17714
|
-
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
|
|
17721
|
-
|
|
17722
|
-
|
|
17723
|
-
|
|
17724
|
-
|
|
17725
|
-
|
|
17726
|
-
|
|
17727
|
-
|
|
17728
|
-
|
|
17729
|
-
|
|
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
|
-
|
|
17733
|
-
|
|
17734
|
-
|
|
17735
|
-
|
|
17736
|
-
|
|
17737
|
-
|
|
17738
|
-
|
|
17739
|
-
|
|
17740
|
-
|
|
17741
|
-
|
|
17742
|
-
|
|
17743
|
-
|
|
17744
|
-
|
|
17745
|
-
|
|
17746
|
-
|
|
17747
|
-
|
|
17748
|
-
|
|
17749
|
-
|
|
17750
|
-
|
|
17751
|
-
|
|
17752
|
-
|
|
17753
|
-
|
|
17754
|
-
|
|
17755
|
-
|
|
17756
|
-
|
|
17757
|
-
|
|
17758
|
-
|
|
17759
|
-
|
|
17760
|
-
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17770
|
-
|
|
17771
|
-
|
|
17772
|
-
|
|
17773
|
-
|
|
17774
|
-
|
|
17775
|
-
|
|
17776
|
-
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
}
|
|
17782
|
-
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
|
|
17786
|
-
|
|
17787
|
-
|
|
17788
|
-
|
|
17789
|
-
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
|
|
17793
|
-
|
|
17794
|
-
|
|
17795
|
-
|
|
17796
|
-
|
|
17797
|
-
|
|
17798
|
-
|
|
17799
|
-
|
|
17800
|
-
|
|
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.
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
translate(e);
|
|
18020
|
+
this.entries = [".", "..", ...this.vfs.readdir(this.path, this.cred)];
|
|
18021
|
+
} catch (error) {
|
|
18022
|
+
throw toPosixError(error);
|
|
17806
18023
|
}
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
|
|
17813
|
-
|
|
17814
|
-
|
|
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
|
|
20714
|
+
const { createRequire } = await nodeBuiltin("module");
|
|
18702
20715
|
const path = await nodeBuiltin("path");
|
|
18703
|
-
const require2 =
|
|
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
|
|
21199
|
+
const { createRequire } = await nodeBuiltin("module");
|
|
19169
21200
|
const fs = await nodeBuiltin("fs/promises");
|
|
19170
21201
|
const path = await nodeBuiltin("path");
|
|
19171
|
-
const packageRequire =
|
|
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 =
|
|
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);
|
|
@@ -20565,7 +22596,9 @@ function safeTarget(destination, name) {
|
|
|
20565
22596
|
return join(destination, clean2);
|
|
20566
22597
|
}
|
|
20567
22598
|
function stripPackageRoot(name) {
|
|
20568
|
-
|
|
22599
|
+
const clean2 = name.replace(/^\.\//, "").replace(/\/$/, "");
|
|
22600
|
+
const slash = clean2.indexOf("/");
|
|
22601
|
+
return slash === -1 ? "" : clean2.slice(slash + 1);
|
|
20569
22602
|
}
|
|
20570
22603
|
function text(bytes2, start2, length) {
|
|
20571
22604
|
return new TextDecoder().decode(bytes2.subarray(start2, start2 + length)).replace(/\0.*$/, "").trim();
|
|
@@ -21700,19 +23733,19 @@ var KernelChildProcess = class {
|
|
|
21700
23733
|
process;
|
|
21701
23734
|
started = false;
|
|
21702
23735
|
cancelled = false;
|
|
21703
|
-
constructor(kernel, cred,
|
|
23736
|
+
constructor(kernel, cred, config2, pid) {
|
|
21704
23737
|
this.stdin.interactive = true;
|
|
21705
|
-
if (
|
|
21706
|
-
if (
|
|
23738
|
+
if (config2.inheritStdio) this.stdin.isTTY = true;
|
|
23739
|
+
if (config2.stdinIgnored) this.stdin.end();
|
|
21707
23740
|
this.stdin.onRawMode = (enabled) => this.emit("rawmode", enabled);
|
|
21708
23741
|
this.kernel = kernel;
|
|
21709
23742
|
this.cred = cred;
|
|
21710
23743
|
this.pid = pid;
|
|
21711
|
-
this.command =
|
|
21712
|
-
this.args =
|
|
21713
|
-
this.parentPid =
|
|
21714
|
-
this.cwd =
|
|
21715
|
-
this.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 };
|
|
21716
23749
|
}
|
|
21717
23750
|
on(event, listener) {
|
|
21718
23751
|
let listeners = this.listeners.get(event);
|
|
@@ -21819,12 +23852,12 @@ var nextBridgePid = 1073741824;
|
|
|
21819
23852
|
function installNodeChildProcessBridge(pod, kernel, cred) {
|
|
21820
23853
|
const manager = pod.processManager;
|
|
21821
23854
|
const originalSpawn = manager.spawn;
|
|
21822
|
-
manager.spawn = (
|
|
21823
|
-
const resolved = kernel.resolveExecutable(
|
|
23855
|
+
manager.spawn = (config2) => {
|
|
23856
|
+
const resolved = kernel.resolveExecutable(config2.command, config2.cwd ?? "/", config2.env ?? {}, cred);
|
|
21824
23857
|
if (resolved?.kind === "builtin") {
|
|
21825
|
-
return new KernelChildProcess(kernel, cred,
|
|
23858
|
+
return new KernelChildProcess(kernel, cred, config2, nextBridgePid++);
|
|
21826
23859
|
}
|
|
21827
|
-
return originalSpawn.call(manager,
|
|
23860
|
+
return originalSpawn.call(manager, config2);
|
|
21828
23861
|
};
|
|
21829
23862
|
return () => {
|
|
21830
23863
|
if (manager.spawn === originalSpawn) return;
|
|
@@ -23186,14 +25219,14 @@ var AssertionError = class extends Error {
|
|
|
23186
25219
|
code = "ERR_ASSERTION";
|
|
23187
25220
|
constructor(options) {
|
|
23188
25221
|
const generated = options.message === void 0;
|
|
23189
|
-
super(options.message ??
|
|
25222
|
+
super(options.message ?? describe2(options.actual, options.expected, options.operator));
|
|
23190
25223
|
this.actual = options.actual;
|
|
23191
25224
|
this.expected = options.expected;
|
|
23192
25225
|
this.operator = options.operator;
|
|
23193
25226
|
this.generatedMessage = generated;
|
|
23194
25227
|
}
|
|
23195
25228
|
};
|
|
23196
|
-
function
|
|
25229
|
+
function describe2(actual, expected, operator) {
|
|
23197
25230
|
if (operator === "fail") return "Failed";
|
|
23198
25231
|
const rendered = (value) => inspect(value, { depth: 2 });
|
|
23199
25232
|
return `${rendered(actual)} ${operator} ${rendered(expected)}`;
|
|
@@ -24031,9 +26064,9 @@ var VirtualClientRequest = class extends streamModule4.Writable {
|
|
|
24031
26064
|
} catch (error) {
|
|
24032
26065
|
clearTimeout(this.timer);
|
|
24033
26066
|
if (this.destroyedByUser) return;
|
|
24034
|
-
const
|
|
24035
|
-
if (!("code" in
|
|
24036
|
-
this.emit("error",
|
|
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);
|
|
24037
26070
|
} finally {
|
|
24038
26071
|
if (!settled) untrack();
|
|
24039
26072
|
}
|
|
@@ -24231,17 +26264,17 @@ function toBytes2(data, encoding) {
|
|
|
24231
26264
|
}
|
|
24232
26265
|
|
|
24233
26266
|
// src/runtime/sync-channel.ts
|
|
24234
|
-
var
|
|
24235
|
-
var
|
|
24236
|
-
var
|
|
24237
|
-
var
|
|
24238
|
-
var
|
|
24239
|
-
var
|
|
24240
|
-
var
|
|
24241
|
-
var
|
|
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;
|
|
24242
26275
|
function createSyncChannelBuffers(capacityBytes = 1 << 20) {
|
|
24243
26276
|
return {
|
|
24244
|
-
control: new SharedArrayBuffer(
|
|
26277
|
+
control: new SharedArrayBuffer(CONTROL_WORDS2 * Int32Array.BYTES_PER_ELEMENT),
|
|
24245
26278
|
data: new SharedArrayBuffer(capacityBytes)
|
|
24246
26279
|
};
|
|
24247
26280
|
}
|
|
@@ -24275,15 +26308,15 @@ var SyncChannelServer = class {
|
|
|
24275
26308
|
/** Call from the wake message the client sends after each chunk. */
|
|
24276
26309
|
async pump() {
|
|
24277
26310
|
if (this.closed) return;
|
|
24278
|
-
const state = Atomics.load(this.control,
|
|
24279
|
-
if (state ===
|
|
24280
|
-
const size = Atomics.load(this.control,
|
|
26311
|
+
const state = Atomics.load(this.control, STATE2);
|
|
26312
|
+
if (state === STATE_REQUEST2) {
|
|
26313
|
+
const size = Atomics.load(this.control, LENGTH2);
|
|
24281
26314
|
this.incoming.push(this.data.slice(0, size));
|
|
24282
|
-
if (Atomics.load(this.control,
|
|
24283
|
-
this.publish(
|
|
26315
|
+
if (Atomics.load(this.control, MORE2) === 1) {
|
|
26316
|
+
this.publish(STATE_CONTINUE2);
|
|
24284
26317
|
return;
|
|
24285
26318
|
}
|
|
24286
|
-
const request =
|
|
26319
|
+
const request = concat4(this.incoming);
|
|
24287
26320
|
this.incoming = [];
|
|
24288
26321
|
let response;
|
|
24289
26322
|
try {
|
|
@@ -24297,30 +26330,30 @@ var SyncChannelServer = class {
|
|
|
24297
26330
|
this.sendChunk();
|
|
24298
26331
|
return;
|
|
24299
26332
|
}
|
|
24300
|
-
if (state ===
|
|
26333
|
+
if (state === STATE_CONTINUE2) this.sendChunk();
|
|
24301
26334
|
}
|
|
24302
26335
|
/** Release a blocked client, e.g. when the container is torn down. */
|
|
24303
26336
|
close() {
|
|
24304
26337
|
if (this.closed) return;
|
|
24305
26338
|
this.closed = true;
|
|
24306
|
-
Atomics.store(this.control,
|
|
24307
|
-
Atomics.notify(this.control,
|
|
26339
|
+
Atomics.store(this.control, STATE2, STATE_CLOSED2);
|
|
26340
|
+
Atomics.notify(this.control, STATE2);
|
|
24308
26341
|
}
|
|
24309
26342
|
sendChunk() {
|
|
24310
26343
|
const payload = this.outgoing ?? new Uint8Array();
|
|
24311
26344
|
const size = Math.min(this.capacity, payload.length - this.sent);
|
|
24312
26345
|
this.data.set(payload.subarray(this.sent, this.sent + size), 0);
|
|
24313
26346
|
this.sent += size;
|
|
24314
|
-
Atomics.store(this.control,
|
|
24315
|
-
Atomics.store(this.control,
|
|
24316
|
-
this.publish(
|
|
26347
|
+
Atomics.store(this.control, LENGTH2, size);
|
|
26348
|
+
Atomics.store(this.control, MORE2, this.sent < payload.length ? 1 : 0);
|
|
26349
|
+
this.publish(STATE_RESPONSE2);
|
|
24317
26350
|
}
|
|
24318
26351
|
publish(state) {
|
|
24319
|
-
Atomics.store(this.control,
|
|
24320
|
-
Atomics.notify(this.control,
|
|
26352
|
+
Atomics.store(this.control, STATE2, state);
|
|
26353
|
+
Atomics.notify(this.control, STATE2);
|
|
24321
26354
|
}
|
|
24322
26355
|
};
|
|
24323
|
-
function
|
|
26356
|
+
function concat4(parts) {
|
|
24324
26357
|
if (parts.length === 1) return parts[0];
|
|
24325
26358
|
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
24326
26359
|
const joined = new Uint8Array(total);
|
|
@@ -24453,13 +26486,13 @@ ${err.join("")}`),
|
|
|
24453
26486
|
return child;
|
|
24454
26487
|
};
|
|
24455
26488
|
const exec = (command, options = {}, callback) => {
|
|
24456
|
-
const [opts, cb] =
|
|
26489
|
+
const [opts, cb] = normalize3(options, callback);
|
|
24457
26490
|
const { file: file3, args } = throughShell(command, opts);
|
|
24458
26491
|
return collect(start2(file3, args, { ...opts, shell: false }), opts, cb);
|
|
24459
26492
|
};
|
|
24460
26493
|
const execFile = (file3, args = [], options = {}, callback) => {
|
|
24461
26494
|
const list = Array.isArray(args) ? args : [];
|
|
24462
|
-
const [opts, cb] = Array.isArray(args) ?
|
|
26495
|
+
const [opts, cb] = Array.isArray(args) ? normalize3(options, callback) : normalize3(args, options);
|
|
24463
26496
|
return collect(start2(file3, list, opts), opts, cb);
|
|
24464
26497
|
};
|
|
24465
26498
|
return {
|
|
@@ -24531,7 +26564,7 @@ ${result.stderr}`), {
|
|
|
24531
26564
|
const execSync = (command, options = {}) => orThrow(run(command, [], { ...options, shell: options.shell ?? true }), command, options);
|
|
24532
26565
|
return { spawnSync, execFileSync, execSync };
|
|
24533
26566
|
}
|
|
24534
|
-
function
|
|
26567
|
+
function normalize3(options, callback) {
|
|
24535
26568
|
if (typeof options === "function") return [{}, options];
|
|
24536
26569
|
return [options ?? {}, callback];
|
|
24537
26570
|
}
|
|
@@ -24923,7 +26956,7 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
|
|
|
24923
26956
|
}
|
|
24924
26957
|
}
|
|
24925
26958
|
}
|
|
24926
|
-
const
|
|
26959
|
+
const isTerminal2 = (options, output) => {
|
|
24927
26960
|
if (typeof options?.terminal === "boolean") return options.terminal;
|
|
24928
26961
|
return Boolean(output?.isTTY);
|
|
24929
26962
|
};
|
|
@@ -24933,13 +26966,13 @@ function createReadlineModule(defaultInput, defaultOutput = () => void 0) {
|
|
|
24933
26966
|
const instance = new Interface(
|
|
24934
26967
|
options.input ?? defaultInput(),
|
|
24935
26968
|
resolvedOutput2,
|
|
24936
|
-
|
|
26969
|
+
isTerminal2(options, resolvedOutput2)
|
|
24937
26970
|
);
|
|
24938
26971
|
if (typeof options.prompt === "string") instance.setPrompt(options.prompt);
|
|
24939
26972
|
return instance;
|
|
24940
26973
|
}
|
|
24941
26974
|
const resolvedOutput = output;
|
|
24942
|
-
return new Interface(options ?? defaultInput(), resolvedOutput,
|
|
26975
|
+
return new Interface(options ?? defaultInput(), resolvedOutput, isTerminal2(void 0, resolvedOutput));
|
|
24943
26976
|
};
|
|
24944
26977
|
const noop = () => {
|
|
24945
26978
|
};
|
|
@@ -26611,15 +28644,91 @@ var MirroringVolume = class {
|
|
|
26611
28644
|
mirror.writeFileSync(path, this.inner.readFileSync(path));
|
|
26612
28645
|
});
|
|
26613
28646
|
}
|
|
26614
|
-
// ── reads: straight through
|
|
28647
|
+
// ── reads: straight through, then the mirror ──────────────────────────────
|
|
28648
|
+
/*
|
|
28649
|
+
* A file the bundler produced exists only in the mirror until something
|
|
28650
|
+
* asks for it here.
|
|
28651
|
+
*
|
|
28652
|
+
* {@link absorb} was the original answer and is not enough on its own. It
|
|
28653
|
+
* runs when a process ends — a dev server never ends — and it skips
|
|
28654
|
+
* `node_modules`, where Vite keeps the one output that a dev server depends
|
|
28655
|
+
* on: `node_modules/.vite/deps`. Between those two, the entire pre-bundled
|
|
28656
|
+
* dependency set stayed invisible to the container that had to serve it.
|
|
28657
|
+
*
|
|
28658
|
+
* The symptom was almost perfectly disguised. Vite writes `_metadata.json`
|
|
28659
|
+
* itself, through this volume, so the directory looked half-populated and
|
|
28660
|
+
* the metadata listed every dependency as optimized. Rolldown wrote the
|
|
28661
|
+
* actual bundles through WASI into the mirror, where nothing brought them
|
|
28662
|
+
* back. Vite then read a file that was not there — and reports a failed read
|
|
28663
|
+
* of an optimized dependency as `504 Outdated Optimize Dep`, which describes
|
|
28664
|
+
* a stale hash and sends everyone looking at hashes. The hash was never
|
|
28665
|
+
* wrong. The file was missing.
|
|
28666
|
+
*
|
|
28667
|
+
* Falling back on read fixes it wherever it happens, for any path, without
|
|
28668
|
+
* polling and without depending on how the engine chose to write.
|
|
28669
|
+
*/
|
|
26615
28670
|
readFileSync(path) {
|
|
26616
|
-
|
|
28671
|
+
try {
|
|
28672
|
+
return this.inner.readFileSync(path);
|
|
28673
|
+
} catch (error) {
|
|
28674
|
+
const produced = this.fromMirror(path);
|
|
28675
|
+
if (!produced) throw error;
|
|
28676
|
+
try {
|
|
28677
|
+
this.ensureDirectory(dirname(path));
|
|
28678
|
+
this.inner.writeFileSync(path, produced);
|
|
28679
|
+
} catch {
|
|
28680
|
+
}
|
|
28681
|
+
return produced;
|
|
28682
|
+
}
|
|
26617
28683
|
}
|
|
26618
28684
|
readdirSync(path) {
|
|
26619
|
-
|
|
28685
|
+
let here = [];
|
|
28686
|
+
let missing;
|
|
28687
|
+
try {
|
|
28688
|
+
here = this.inner.readdirSync(path);
|
|
28689
|
+
} catch (error) {
|
|
28690
|
+
missing = error;
|
|
28691
|
+
}
|
|
28692
|
+
const mirrored = this.mirrorNames(path);
|
|
28693
|
+
if (!mirrored) {
|
|
28694
|
+
if (missing) throw missing;
|
|
28695
|
+
return here;
|
|
28696
|
+
}
|
|
28697
|
+
return [.../* @__PURE__ */ new Set([...here, ...mirrored])].sort();
|
|
26620
28698
|
}
|
|
26621
28699
|
lstatSync(path) {
|
|
26622
|
-
|
|
28700
|
+
try {
|
|
28701
|
+
return this.inner.lstatSync(path);
|
|
28702
|
+
} catch (error) {
|
|
28703
|
+
if (this.fromMirror(path)) {
|
|
28704
|
+
try {
|
|
28705
|
+
this.readFileSync(path);
|
|
28706
|
+
return this.inner.lstatSync(path);
|
|
28707
|
+
} catch {
|
|
28708
|
+
}
|
|
28709
|
+
}
|
|
28710
|
+
throw error;
|
|
28711
|
+
}
|
|
28712
|
+
}
|
|
28713
|
+
/** The mirror's copy of a file, if it has one and we are allowed to look. */
|
|
28714
|
+
fromMirror(path) {
|
|
28715
|
+
const mirror = this.mirror;
|
|
28716
|
+
if (!mirror?.readFileSync || !mirror.lstatSync || !this.mirrored(path)) return void 0;
|
|
28717
|
+
try {
|
|
28718
|
+
if (!mirror.lstatSync(path).isDirectory()) return mirror.readFileSync(path);
|
|
28719
|
+
} catch {
|
|
28720
|
+
}
|
|
28721
|
+
return void 0;
|
|
28722
|
+
}
|
|
28723
|
+
/** The mirror's entries for a directory, for merging into a listing. */
|
|
28724
|
+
mirrorNames(path) {
|
|
28725
|
+
const mirror = this.mirror;
|
|
28726
|
+
if (!mirror?.readdirSync || !this.mirrored(path)) return void 0;
|
|
28727
|
+
try {
|
|
28728
|
+
return mirror.readdirSync(path);
|
|
28729
|
+
} catch {
|
|
28730
|
+
return void 0;
|
|
28731
|
+
}
|
|
26623
28732
|
}
|
|
26624
28733
|
readlinkSync(path) {
|
|
26625
28734
|
return this.inner.readlinkSync(path);
|
|
@@ -26648,11 +28757,76 @@ var MirroringVolume = class {
|
|
|
26648
28757
|
this.inner.unlinkSync(path);
|
|
26649
28758
|
this.sync(path);
|
|
26650
28759
|
}
|
|
28760
|
+
/**
|
|
28761
|
+
* Rename, carrying anything the mirrored engine put there.
|
|
28762
|
+
*
|
|
28763
|
+
* This is the operation the dependency optimizer is built on, and the one
|
|
28764
|
+
* that used to destroy its output. Vite bundles into a temporary directory
|
|
28765
|
+
* and renames it into place when it is complete — but only half of that
|
|
28766
|
+
* directory is ever on this volume. Vite writes `_metadata.json` and
|
|
28767
|
+
* `package.json` through here; Rolldown writes the actual bundles through
|
|
28768
|
+
* WASI into the mirror. Renaming moved the half that was here and then
|
|
28769
|
+
* `sync(from)` deleted the half that was not, because from this side the
|
|
28770
|
+
* source directory had simply gone.
|
|
28771
|
+
*
|
|
28772
|
+
* What survived was a `deps` directory holding metadata that described five
|
|
28773
|
+
* optimized dependencies and contained none of them — which Vite reports, on
|
|
28774
|
+
* the failed read, as `504 Outdated Optimize Dep`. Nothing in that message is
|
|
28775
|
+
* true, and following it leads to hashes rather than to a missing file.
|
|
28776
|
+
*
|
|
28777
|
+
* So the mirror's side of the move is collected first, and lands here as
|
|
28778
|
+
* part of the rename.
|
|
28779
|
+
*/
|
|
26651
28780
|
renameSync(from, to) {
|
|
26652
28781
|
this.inner.renameSync(from, to);
|
|
28782
|
+
this.collect(from, to);
|
|
26653
28783
|
this.sync(from);
|
|
26654
28784
|
this.sync(to);
|
|
26655
28785
|
}
|
|
28786
|
+
/**
|
|
28787
|
+
* Copy whatever the mirror holds under `from` into this volume under `to`.
|
|
28788
|
+
*
|
|
28789
|
+
* Deliberately not filtered the way {@link absorb} is: this walks one
|
|
28790
|
+
* directory that is being renamed, not the whole project, so the subtree is
|
|
28791
|
+
* small and `node_modules` is exactly where the interesting case lives.
|
|
28792
|
+
*/
|
|
28793
|
+
collect(from, to) {
|
|
28794
|
+
const mirror = this.mirror;
|
|
28795
|
+
if (!mirror?.readdirSync || !mirror.readFileSync || !mirror.lstatSync) return;
|
|
28796
|
+
if (!this.mirrored(from)) return;
|
|
28797
|
+
const walk = (source, destination) => {
|
|
28798
|
+
let stat2;
|
|
28799
|
+
try {
|
|
28800
|
+
stat2 = mirror.lstatSync(source);
|
|
28801
|
+
} catch {
|
|
28802
|
+
return;
|
|
28803
|
+
}
|
|
28804
|
+
if (stat2.isSymbolicLink()) return;
|
|
28805
|
+
if (stat2.isDirectory()) {
|
|
28806
|
+
let names;
|
|
28807
|
+
try {
|
|
28808
|
+
names = mirror.readdirSync(source);
|
|
28809
|
+
} catch {
|
|
28810
|
+
return;
|
|
28811
|
+
}
|
|
28812
|
+
this.ensureDirectory(destination);
|
|
28813
|
+
for (const name of names) walk(join(source, name), join(destination, name));
|
|
28814
|
+
return;
|
|
28815
|
+
}
|
|
28816
|
+
try {
|
|
28817
|
+
this.inner.lstatSync(destination);
|
|
28818
|
+
return;
|
|
28819
|
+
} catch {
|
|
28820
|
+
}
|
|
28821
|
+
try {
|
|
28822
|
+
const bytes2 = mirror.readFileSync(source);
|
|
28823
|
+
this.ensureDirectory(dirname(destination));
|
|
28824
|
+
this.inner.writeFileSync(destination, bytes2);
|
|
28825
|
+
} catch {
|
|
28826
|
+
}
|
|
28827
|
+
};
|
|
28828
|
+
this.safely(() => walk(from, to));
|
|
28829
|
+
}
|
|
26656
28830
|
symlinkSync(target, path) {
|
|
26657
28831
|
this.inner.symlinkSync(target, path);
|
|
26658
28832
|
this.sync(path);
|
|
@@ -26765,44 +28939,69 @@ function ensureProcessGlobal() {
|
|
|
26765
28939
|
configurable: true
|
|
26766
28940
|
});
|
|
26767
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
|
+
}
|
|
26768
28961
|
var BINDING_PACKAGE = "@rolldown/binding-wasm32-wasi";
|
|
26769
28962
|
var ASYNC_WORK_POOL_SIZE = 4;
|
|
26770
28963
|
var WORKER_FILE = "rolldown-wasi-worker.js";
|
|
26771
28964
|
async function loadNodeRolldownMemfsBinding() {
|
|
28965
|
+
const node2 = await loadNodeApi();
|
|
28966
|
+
const { createRequire, existsSync, readFileSync, dirname: dirname3, join: join3, fileURLToPath: fileURLToPath2, pathToFileURL: pathToFileURL2 } = node2;
|
|
26772
28967
|
const require2 = createRequire(import.meta.url);
|
|
26773
28968
|
let packageDir;
|
|
26774
28969
|
try {
|
|
26775
|
-
packageDir =
|
|
28970
|
+
packageDir = dirname3(require2.resolve(`${BINDING_PACKAGE}/package.json`));
|
|
26776
28971
|
} catch {
|
|
26777
28972
|
return null;
|
|
26778
28973
|
}
|
|
26779
|
-
const fromBinding = createRequire(
|
|
28974
|
+
const fromBinding = createRequire(join3(packageDir, "package.json"));
|
|
26780
28975
|
let runtime;
|
|
26781
28976
|
let fsModuleUrl;
|
|
26782
28977
|
let createContext2;
|
|
26783
28978
|
try {
|
|
26784
28979
|
runtime = fromBinding("@napi-rs/wasm-runtime");
|
|
26785
|
-
fsModuleUrl = resolveSubpath(fromBinding, "@napi-rs/wasm-runtime", "./fs");
|
|
28980
|
+
fsModuleUrl = resolveSubpath(node2, fromBinding, "@napi-rs/wasm-runtime", "./fs");
|
|
26786
28981
|
({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
|
|
26787
28982
|
} catch {
|
|
26788
28983
|
return null;
|
|
26789
28984
|
}
|
|
26790
|
-
const { memfs } = await import(
|
|
26791
|
-
|
|
28985
|
+
const { memfs } = await import(
|
|
28986
|
+
/* @vite-ignore */
|
|
28987
|
+
/* webpackIgnore: true */
|
|
28988
|
+
fsModuleUrl
|
|
28989
|
+
);
|
|
28990
|
+
const { Worker: Worker2 } = await nodeBuiltin("worker_threads");
|
|
26792
28991
|
const { fs: memoryFs, vol } = memfs();
|
|
26793
28992
|
const wasi2 = new runtime.WASI({
|
|
26794
28993
|
version: "preview1",
|
|
26795
28994
|
fs: memoryFs,
|
|
26796
28995
|
preopens: { "/": "/" }
|
|
26797
28996
|
});
|
|
26798
|
-
const wasmFile = readFileSync(
|
|
28997
|
+
const wasmFile = readFileSync(join3(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
|
|
26799
28998
|
const sharedMemory = new WebAssembly.Memory({ initial: 16384, maximum: 65536, shared: true });
|
|
26800
28999
|
const workerPoolSize = Math.max(2, cpuCount());
|
|
26801
29000
|
const context = createContext2({ autoDestroy: false });
|
|
26802
29001
|
context.suppressDestroy();
|
|
26803
|
-
const workerPath = resolveWorkerPath();
|
|
29002
|
+
const workerPath = resolveWorkerPath(node2);
|
|
26804
29003
|
if (!workerPath) return null;
|
|
26805
|
-
const runtimeAnchor =
|
|
29004
|
+
const runtimeAnchor = pathToFileURL2(join3(packageDir, "package.json")).href;
|
|
26806
29005
|
const workers = /* @__PURE__ */ new Set();
|
|
26807
29006
|
let started = false;
|
|
26808
29007
|
const { napiModule } = await runtime.instantiateNapiModule(wasmFile, {
|
|
@@ -26854,24 +29053,26 @@ async function loadNodeRolldownMemfsBinding() {
|
|
|
26854
29053
|
for (const worker of workers) unreferenceWorker(worker);
|
|
26855
29054
|
return { ...napiModule.exports, __fs: memoryFs, __volume: vol };
|
|
26856
29055
|
}
|
|
26857
|
-
function resolveWorkerPath() {
|
|
26858
|
-
const
|
|
29056
|
+
function resolveWorkerPath(node2) {
|
|
29057
|
+
const { dirname: dirname3, existsSync, fileURLToPath: fileURLToPath2, join: join3 } = node2;
|
|
29058
|
+
const here = dirname3(fileURLToPath2(import.meta.url));
|
|
26859
29059
|
const candidates = [
|
|
26860
|
-
|
|
29060
|
+
join3(here, WORKER_FILE),
|
|
26861
29061
|
/* src/runtime → dist, for a checkout that has been built. */
|
|
26862
|
-
|
|
29062
|
+
join3(here, "..", "..", "dist", WORKER_FILE)
|
|
26863
29063
|
];
|
|
26864
29064
|
return candidates.find((candidate) => existsSync(candidate)) ?? null;
|
|
26865
29065
|
}
|
|
26866
|
-
function resolveSubpath(require2, packageName, subpath) {
|
|
26867
|
-
const
|
|
26868
|
-
const
|
|
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"));
|
|
26869
29070
|
const entry = manifest?.exports?.[subpath];
|
|
26870
29071
|
const target = typeof entry === "string" ? entry : entry?.import ?? entry?.default;
|
|
26871
29072
|
if (typeof target !== "string") {
|
|
26872
29073
|
throw new Error(`${packageName} does not export ${subpath}`);
|
|
26873
29074
|
}
|
|
26874
|
-
return
|
|
29075
|
+
return pathToFileURL2(join3(packageDir, target)).href;
|
|
26875
29076
|
}
|
|
26876
29077
|
function cpuCount() {
|
|
26877
29078
|
try {
|
|
@@ -27050,9 +29251,9 @@ var LocalProcess = class extends EventEmitter4 {
|
|
|
27050
29251
|
}
|
|
27051
29252
|
};
|
|
27052
29253
|
var PodChildProcess = class {
|
|
27053
|
-
constructor(pid,
|
|
29254
|
+
constructor(pid, config2, launch) {
|
|
27054
29255
|
this.pid = pid;
|
|
27055
|
-
this.config =
|
|
29256
|
+
this.config = config2;
|
|
27056
29257
|
this.launch = launch;
|
|
27057
29258
|
}
|
|
27058
29259
|
pid;
|
|
@@ -27190,15 +29391,54 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
27190
29391
|
instanceId = `sbx-${Math.random().toString(36).slice(2)}`;
|
|
27191
29392
|
router = new VirtualHttpRouter();
|
|
27192
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
|
+
}
|
|
27193
29433
|
/**
|
|
27194
29434
|
* Mutable on purpose: a container replaces `spawn` so that children resolve
|
|
27195
29435
|
* against the kernel's PATH. Left alone, it runs `node` and reports anything
|
|
27196
29436
|
* else as not found, which is the correct answer for a bare pod.
|
|
27197
29437
|
*/
|
|
27198
29438
|
processManager = {
|
|
27199
|
-
spawn: (
|
|
29439
|
+
spawn: (config2) => new PodChildProcess(
|
|
27200
29440
|
nextChildPid++,
|
|
27201
|
-
|
|
29441
|
+
config2,
|
|
27202
29442
|
(child) => this.spawn(child.command, child.args ?? [], {
|
|
27203
29443
|
...child.cwd ? { cwd: child.cwd } : {},
|
|
27204
29444
|
...child.env ? { env: child.env } : {}
|
|
@@ -27271,7 +29511,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
27271
29511
|
if (engine?.isEvaluating) throw new ProcessExit(code);
|
|
27272
29512
|
},
|
|
27273
29513
|
http: { router: this.router, owner, ...this.fetch ? { fetch: this.fetch } : {} },
|
|
27274
|
-
spawnChild: (
|
|
29514
|
+
spawnChild: (config2) => this.processManager.spawn(config2),
|
|
27275
29515
|
...typeof options.stdinPath === "string" ? { stdinPath: options.stdinPath } : {},
|
|
27276
29516
|
...options.interactiveStdin ? { interactiveStdin: true } : {},
|
|
27277
29517
|
...options.tty ? { tty: true } : {},
|
|
@@ -27492,10 +29732,10 @@ function serveVolume(volume) {
|
|
|
27492
29732
|
}
|
|
27493
29733
|
return encodeFrame({ ok: true, value: result });
|
|
27494
29734
|
} catch (error) {
|
|
27495
|
-
const
|
|
29735
|
+
const failure2 = error;
|
|
27496
29736
|
return encodeFrame({
|
|
27497
29737
|
ok: false,
|
|
27498
|
-
error: { ...
|
|
29738
|
+
error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
|
|
27499
29739
|
});
|
|
27500
29740
|
}
|
|
27501
29741
|
};
|
|
@@ -27512,7 +29752,7 @@ function serveSyncSyscalls(options) {
|
|
|
27512
29752
|
const result = await options.spawnChild(header.args[0]);
|
|
27513
29753
|
return encodeFrame({ ok: true, value: result });
|
|
27514
29754
|
} catch (error) {
|
|
27515
|
-
const
|
|
29755
|
+
const failure2 = error;
|
|
27516
29756
|
return encodeFrame({
|
|
27517
29757
|
ok: true,
|
|
27518
29758
|
value: {
|
|
@@ -27520,81 +29760,10 @@ function serveSyncSyscalls(options) {
|
|
|
27520
29760
|
stdout: "",
|
|
27521
29761
|
stderr: "",
|
|
27522
29762
|
signal: null,
|
|
27523
|
-
error: { ...
|
|
27524
|
-
}
|
|
27525
|
-
});
|
|
27526
|
-
}
|
|
27527
|
-
};
|
|
27528
|
-
}
|
|
27529
|
-
|
|
27530
|
-
// src/runtime/worker-host.ts
|
|
27531
|
-
function defaultWorkerUrl() {
|
|
27532
|
-
return new URL("./worker-entry.js", import.meta.url);
|
|
27533
|
-
}
|
|
27534
|
-
function hasDomWorker() {
|
|
27535
|
-
return typeof Worker === "function";
|
|
27536
|
-
}
|
|
27537
|
-
async function startRuntimeWorker(options = {}) {
|
|
27538
|
-
const url = options.url ?? defaultWorkerUrl();
|
|
27539
|
-
const worker = hasDomWorker() ? await startDomWorker(url) : await startNodeWorker(url, options.workerData ?? {});
|
|
27540
|
-
try {
|
|
27541
|
-
await new Promise((resolve2, reject) => {
|
|
27542
|
-
const timer = setTimeout(() => reject(new Error("the runtime worker did not start in time")), options.timeoutMs ?? 1e4);
|
|
27543
|
-
worker.onMessage((message) => {
|
|
27544
|
-
if (message?.type === "sandboxedjs:ready") {
|
|
27545
|
-
clearTimeout(timer);
|
|
27546
|
-
resolve2();
|
|
29763
|
+
error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message ?? String(error) }
|
|
27547
29764
|
}
|
|
27548
29765
|
});
|
|
27549
|
-
worker.onError((error) => {
|
|
27550
|
-
clearTimeout(timer);
|
|
27551
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
27552
|
-
});
|
|
27553
|
-
});
|
|
27554
|
-
} catch (error) {
|
|
27555
|
-
try {
|
|
27556
|
-
await worker.terminate();
|
|
27557
|
-
} catch {
|
|
27558
29766
|
}
|
|
27559
|
-
throw error;
|
|
27560
|
-
}
|
|
27561
|
-
return worker;
|
|
27562
|
-
}
|
|
27563
|
-
async function startDomWorker(url) {
|
|
27564
|
-
const worker = new Worker(url, { type: "module" });
|
|
27565
|
-
const listeners = [];
|
|
27566
|
-
const errors = [];
|
|
27567
|
-
worker.addEventListener("message", (event) => {
|
|
27568
|
-
for (const listener of listeners) listener(event.data);
|
|
27569
|
-
});
|
|
27570
|
-
worker.addEventListener("error", (event) => {
|
|
27571
|
-
const detail = event.message || "the runtime worker failed to load";
|
|
27572
|
-
for (const listener of errors) listener(new Error(detail));
|
|
27573
|
-
});
|
|
27574
|
-
return {
|
|
27575
|
-
postMessage: (message) => worker.postMessage(message),
|
|
27576
|
-
onMessage: (listener) => {
|
|
27577
|
-
listeners.push(listener);
|
|
27578
|
-
},
|
|
27579
|
-
onError: (listener) => {
|
|
27580
|
-
errors.push(listener);
|
|
27581
|
-
},
|
|
27582
|
-
terminate: () => worker.terminate()
|
|
27583
|
-
};
|
|
27584
|
-
}
|
|
27585
|
-
async function startNodeWorker(url, workerData) {
|
|
27586
|
-
const specifier = ["node", "worker_threads"].join(":");
|
|
27587
|
-
const { Worker: NodeWorker } = await import(
|
|
27588
|
-
/* @vite-ignore */
|
|
27589
|
-
/* webpackIgnore: true */
|
|
27590
|
-
specifier
|
|
27591
|
-
);
|
|
27592
|
-
const worker = new NodeWorker(url, { workerData });
|
|
27593
|
-
return {
|
|
27594
|
-
postMessage: (message) => worker.postMessage(message),
|
|
27595
|
-
onMessage: (listener) => worker.on("message", listener),
|
|
27596
|
-
onError: (listener) => worker.on("error", listener),
|
|
27597
|
-
terminate: () => worker.terminate()
|
|
27598
29767
|
};
|
|
27599
29768
|
}
|
|
27600
29769
|
var WorkerProcess = class extends EventEmitter4 {
|
|
@@ -27858,8 +30027,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27858
30027
|
...request.inheritStdio ? { inheritStdio: true } : {}
|
|
27859
30028
|
});
|
|
27860
30029
|
} catch (error) {
|
|
27861
|
-
const
|
|
27862
|
-
resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...
|
|
30030
|
+
const failure2 = error;
|
|
30031
|
+
resolve2({ status: null, stdout: "", stderr: "", signal: null, error: { ...failure2.code ? { code: failure2.code } : {}, message: failure2.message } });
|
|
27863
30032
|
return;
|
|
27864
30033
|
}
|
|
27865
30034
|
owned.add(handle);
|
|
@@ -27914,7 +30083,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27914
30083
|
async forward(worker, port, request, response) {
|
|
27915
30084
|
const chunks = [];
|
|
27916
30085
|
for await (const chunk of request) chunks.push(chunk);
|
|
27917
|
-
const body =
|
|
30086
|
+
const body = concat5(chunks);
|
|
27918
30087
|
const id = this.nextRequestId++;
|
|
27919
30088
|
const answered = new Promise((resolve2) => this.waiting.set(id, resolve2));
|
|
27920
30089
|
worker.postMessage({
|
|
@@ -27995,7 +30164,7 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27995
30164
|
function isRecord2(value) {
|
|
27996
30165
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
27997
30166
|
}
|
|
27998
|
-
function
|
|
30167
|
+
function concat5(parts) {
|
|
27999
30168
|
if (parts.length === 0) return new Uint8Array();
|
|
28000
30169
|
if (parts.length === 1) return parts[0];
|
|
28001
30170
|
const total = parts.reduce((sum, part) => sum + part.length, 0);
|
|
@@ -29132,6 +31301,6 @@ async function renderInto(box, element, options = { port: 80 }) {
|
|
|
29132
31301
|
// src/index.ts
|
|
29133
31302
|
var src_default = createContainer;
|
|
29134
31303
|
|
|
29135
|
-
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 };
|
|
29136
31305
|
//# sourceMappingURL=index.js.map
|
|
29137
31306
|
//# sourceMappingURL=index.js.map
|