sandboxedjs 0.1.33 → 0.1.35
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/README.md +42 -0
- package/bin/sandboxedjs.mjs +3 -1
- package/dist/index.cjs +1270 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +238 -1
- package/dist/index.d.ts +238 -1
- package/dist/index.js +1265 -40
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -774,10 +774,10 @@ function globToRegexSource(pattern, opts = {}) {
|
|
|
774
774
|
continue;
|
|
775
775
|
}
|
|
776
776
|
if (c === "[") {
|
|
777
|
-
const
|
|
778
|
-
if (
|
|
779
|
-
out += guardDot() +
|
|
780
|
-
i =
|
|
777
|
+
const compiled3 = compileBracket(pattern, i);
|
|
778
|
+
if (compiled3) {
|
|
779
|
+
out += guardDot() + compiled3.source;
|
|
780
|
+
i = compiled3.next;
|
|
781
781
|
atSegmentStart = false;
|
|
782
782
|
continue;
|
|
783
783
|
}
|
|
@@ -5690,8 +5690,101 @@ function strerrorOf(e) {
|
|
|
5690
5690
|
var EXIT_NOT_EXECUTABLE = 126;
|
|
5691
5691
|
var EXIT_NOT_FOUND = 127;
|
|
5692
5692
|
|
|
5693
|
+
// src/wasi/constants.ts
|
|
5694
|
+
var WASI_ESUCCESS = 0;
|
|
5695
|
+
var WASI_EACCES = 2;
|
|
5696
|
+
var WASI_EAGAIN = 6;
|
|
5697
|
+
var WASI_EBADF = 8;
|
|
5698
|
+
var WASI_EBUSY = 10;
|
|
5699
|
+
var WASI_EEXIST = 20;
|
|
5700
|
+
var WASI_EFAULT = 21;
|
|
5701
|
+
var WASI_EFBIG = 22;
|
|
5702
|
+
var WASI_EINVAL = 28;
|
|
5703
|
+
var WASI_EIO = 29;
|
|
5704
|
+
var WASI_EISDIR = 31;
|
|
5705
|
+
var WASI_ELOOP = 32;
|
|
5706
|
+
var WASI_EMFILE = 34;
|
|
5707
|
+
var WASI_ENAMETOOLONG = 37;
|
|
5708
|
+
var WASI_ENOENT = 44;
|
|
5709
|
+
var WASI_ENOMEM = 48;
|
|
5710
|
+
var WASI_ENOSPC = 51;
|
|
5711
|
+
var WASI_ENOSYS = 52;
|
|
5712
|
+
var WASI_ENOTDIR = 54;
|
|
5713
|
+
var WASI_ENOTEMPTY = 55;
|
|
5714
|
+
var WASI_ENOTSUP = 58;
|
|
5715
|
+
var WASI_ENOTTY = 59;
|
|
5716
|
+
var WASI_EPERM = 63;
|
|
5717
|
+
var WASI_EPIPE = 64;
|
|
5718
|
+
var WASI_EROFS = 69;
|
|
5719
|
+
var WASI_ESPIPE = 70;
|
|
5720
|
+
var WASI_EXDEV = 75;
|
|
5721
|
+
var WASI_ENOTCAPABLE = 76;
|
|
5722
|
+
var WASI_ERRNO_BY_CODE = {
|
|
5723
|
+
EPERM: WASI_EPERM,
|
|
5724
|
+
ENOENT: WASI_ENOENT,
|
|
5725
|
+
EIO: WASI_EIO,
|
|
5726
|
+
EBADF: WASI_EBADF,
|
|
5727
|
+
EAGAIN: WASI_EAGAIN,
|
|
5728
|
+
ENOMEM: WASI_ENOMEM,
|
|
5729
|
+
EACCES: WASI_EACCES,
|
|
5730
|
+
EFAULT: WASI_EFAULT,
|
|
5731
|
+
EBUSY: WASI_EBUSY,
|
|
5732
|
+
EEXIST: WASI_EEXIST,
|
|
5733
|
+
EXDEV: WASI_EXDEV,
|
|
5734
|
+
ENOTDIR: WASI_ENOTDIR,
|
|
5735
|
+
EISDIR: WASI_EISDIR,
|
|
5736
|
+
EINVAL: WASI_EINVAL,
|
|
5737
|
+
EMFILE: WASI_EMFILE,
|
|
5738
|
+
ENOTTY: WASI_ENOTTY,
|
|
5739
|
+
EFBIG: WASI_EFBIG,
|
|
5740
|
+
ENOSPC: WASI_ENOSPC,
|
|
5741
|
+
ESPIPE: WASI_ESPIPE,
|
|
5742
|
+
EROFS: WASI_EROFS,
|
|
5743
|
+
EPIPE: WASI_EPIPE,
|
|
5744
|
+
ENAMETOOLONG: WASI_ENAMETOOLONG,
|
|
5745
|
+
ENOSYS: WASI_ENOSYS,
|
|
5746
|
+
ENOTEMPTY: WASI_ENOTEMPTY,
|
|
5747
|
+
ELOOP: WASI_ELOOP,
|
|
5748
|
+
ENOTSUP: WASI_ENOTSUP
|
|
5749
|
+
};
|
|
5750
|
+
var FILETYPE_UNKNOWN = 0;
|
|
5751
|
+
var FILETYPE_CHARACTER_DEVICE = 2;
|
|
5752
|
+
var FILETYPE_DIRECTORY = 3;
|
|
5753
|
+
var FILETYPE_REGULAR_FILE = 4;
|
|
5754
|
+
var FILETYPE_SYMBOLIC_LINK = 7;
|
|
5755
|
+
var O_CREAT = 1 << 0;
|
|
5756
|
+
var O_DIRECTORY = 1 << 1;
|
|
5757
|
+
var O_EXCL = 1 << 2;
|
|
5758
|
+
var O_TRUNC = 1 << 3;
|
|
5759
|
+
var FD_APPEND = 1 << 0;
|
|
5760
|
+
var LOOKUP_SYMLINK_FOLLOW = 1 << 0;
|
|
5761
|
+
var FST_ATIM = 1 << 0;
|
|
5762
|
+
var FST_ATIM_NOW = 1 << 1;
|
|
5763
|
+
var FST_MTIM = 1 << 2;
|
|
5764
|
+
var FST_MTIM_NOW = 1 << 3;
|
|
5765
|
+
var WHENCE_SET = 0;
|
|
5766
|
+
var WHENCE_CUR = 1;
|
|
5767
|
+
var WHENCE_END = 2;
|
|
5768
|
+
var EVENTTYPE_CLOCK = 0;
|
|
5769
|
+
var EVENTTYPE_FD_READ = 1;
|
|
5770
|
+
var EVENTTYPE_FD_WRITE = 2;
|
|
5771
|
+
var SUBSCRIPTION_CLOCK_ABSTIME = 1 << 0;
|
|
5772
|
+
var RIGHT_FD_READ = 1n << 1n;
|
|
5773
|
+
var RIGHT_FD_SEEK = 1n << 2n;
|
|
5774
|
+
var RIGHT_FD_WRITE = 1n << 6n;
|
|
5775
|
+
var RIGHTS_ALL = (1n << 30n) - 1n;
|
|
5776
|
+
var SIZEOF_FILESTAT = 64;
|
|
5777
|
+
var SIZEOF_FDSTAT = 24;
|
|
5778
|
+
var SIZEOF_DIRENT = 24;
|
|
5779
|
+
var SIZEOF_SUBSCRIPTION = 48;
|
|
5780
|
+
var SIZEOF_EVENT = 32;
|
|
5781
|
+
function isWasmBinary(bytes2) {
|
|
5782
|
+
return bytes2.length >= 4 && bytes2[0] === 0 && bytes2[1] === 97 && bytes2[2] === 115 && bytes2[3] === 109;
|
|
5783
|
+
}
|
|
5784
|
+
|
|
5693
5785
|
// src/kernel/kernel.ts
|
|
5694
5786
|
var BUILTIN_INTERPRETER = "/proc/self/exe";
|
|
5787
|
+
var WASI_INTERPRETER = "/usr/bin/wasi";
|
|
5695
5788
|
var Kernel = class {
|
|
5696
5789
|
vfs;
|
|
5697
5790
|
procs = new ProcessTable();
|
|
@@ -5826,12 +5919,17 @@ var Kernel = class {
|
|
|
5826
5919
|
if (command) return { kind: "builtin", path: candidate, command };
|
|
5827
5920
|
}
|
|
5828
5921
|
let head2 = "";
|
|
5922
|
+
let magic;
|
|
5829
5923
|
try {
|
|
5830
5924
|
if (this.vfs.permitted(st, R_OK, cred)) {
|
|
5831
|
-
|
|
5925
|
+
magic = this.vfs.readFile(real, cred).subarray(0, 256);
|
|
5926
|
+
head2 = new TextDecoder().decode(magic);
|
|
5832
5927
|
}
|
|
5833
5928
|
} catch {
|
|
5834
5929
|
}
|
|
5930
|
+
if (magic && isWasmBinary(magic)) {
|
|
5931
|
+
return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
|
|
5932
|
+
}
|
|
5835
5933
|
if (head2.startsWith("#!")) {
|
|
5836
5934
|
const lineEnd = head2.indexOf("\n");
|
|
5837
5935
|
const line = (lineEnd >= 0 ? head2.slice(2, lineEnd) : head2.slice(2)).trim();
|
|
@@ -5845,6 +5943,7 @@ var Kernel = class {
|
|
|
5845
5943
|
const ext = extname(real);
|
|
5846
5944
|
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return { kind: "script", path: real, interpreter: ["/usr/bin/node"] };
|
|
5847
5945
|
if (ext === ".py") return { kind: "script", path: real, interpreter: ["/usr/bin/python3"] };
|
|
5946
|
+
if (ext === ".wasm") return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
|
|
5848
5947
|
return { kind: "script", path: real, interpreter: ["/bin/sh"] };
|
|
5849
5948
|
}
|
|
5850
5949
|
return null;
|
|
@@ -15723,9 +15822,9 @@ function makeSum(name, algorithm, path) {
|
|
|
15723
15822
|
if (args.has("check")) return checkSums(ctx, algorithm, args.positional, args.has("quiet") || args.has("status"));
|
|
15724
15823
|
const { sources, ok: ok2 } = await readInputs(ctx, args.positional, { stdinName: "-" });
|
|
15725
15824
|
for (const source of sources) {
|
|
15726
|
-
const
|
|
15727
|
-
if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${
|
|
15728
|
-
else ctx.line(`${
|
|
15825
|
+
const digest2 = await digestHex(algorithm, source.bytes);
|
|
15826
|
+
if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${digest2}`);
|
|
15827
|
+
else ctx.line(`${digest2} ${source.name}`);
|
|
15729
15828
|
}
|
|
15730
15829
|
return ok2 ? 0 : 1;
|
|
15731
15830
|
}
|
|
@@ -18168,6 +18267,55 @@ def _install_processes():
|
|
|
18168
18267
|
|
|
18169
18268
|
_install_processes()
|
|
18170
18269
|
`;
|
|
18270
|
+
var ASYNCIO_PY = `
|
|
18271
|
+
import asyncio
|
|
18272
|
+
from pyodide.ffi import can_run_sync, run_sync
|
|
18273
|
+
|
|
18274
|
+
_orig_run = asyncio.run
|
|
18275
|
+
|
|
18276
|
+
|
|
18277
|
+
def _run(main, *, debug=None, loop_factory=None):
|
|
18278
|
+
if not asyncio.iscoroutine(main) and not asyncio.isfuture(main):
|
|
18279
|
+
raise ValueError("a coroutine was expected, got {!r}".format(main))
|
|
18280
|
+
if not can_run_sync():
|
|
18281
|
+
# No stack switching: the original at least raises a comprehensible
|
|
18282
|
+
# error rather than this shim inventing a new one.
|
|
18283
|
+
return _orig_run(main, debug=debug, loop_factory=loop_factory)
|
|
18284
|
+
return run_sync(main)
|
|
18285
|
+
|
|
18286
|
+
|
|
18287
|
+
def _run_until_complete(self, future):
|
|
18288
|
+
if not can_run_sync():
|
|
18289
|
+
raise RuntimeError(
|
|
18290
|
+
"cannot wait for a coroutine on this host: WebAssembly stack "
|
|
18291
|
+
"switching (JSPI) is unavailable."
|
|
18292
|
+
)
|
|
18293
|
+
return run_sync(future)
|
|
18294
|
+
|
|
18295
|
+
|
|
18296
|
+
async def _create_server(self, *args, **kwargs):
|
|
18297
|
+
# Pyodide's loop raises a bare NotImplementedError from deep inside
|
|
18298
|
+
# asyncio, which tells a reader nothing about why their server will not
|
|
18299
|
+
# start. Naming the limit is the least this can do until the container can
|
|
18300
|
+
# route connections to Python.
|
|
18301
|
+
raise NotImplementedError(
|
|
18302
|
+
"this container cannot yet accept network connections from Python: "
|
|
18303
|
+
"asyncio's create_server is not implemented on Pyodide's event loop, "
|
|
18304
|
+
"so an ASGI/WSGI server such as uvicorn will install and import but "
|
|
18305
|
+
"not bind a port. Outbound requests do work."
|
|
18306
|
+
)
|
|
18307
|
+
|
|
18308
|
+
|
|
18309
|
+
def _install_asyncio():
|
|
18310
|
+
asyncio.run = _run
|
|
18311
|
+
asyncio.runners.run = _run
|
|
18312
|
+
loop_type = type(asyncio.get_event_loop())
|
|
18313
|
+
loop_type.run_until_complete = _run_until_complete
|
|
18314
|
+
loop_type.create_server = _create_server
|
|
18315
|
+
|
|
18316
|
+
|
|
18317
|
+
_install_asyncio()
|
|
18318
|
+
`;
|
|
18171
18319
|
var NETWORK_PY = `
|
|
18172
18320
|
import builtins, email.message, io, sys, urllib.error, urllib.request, urllib.response
|
|
18173
18321
|
import _sbx_host as _host
|
|
@@ -18452,16 +18600,17 @@ function installSyscallBridge(py) {
|
|
|
18452
18600
|
py.runPython(BRIDGE_PY);
|
|
18453
18601
|
py.runPython(PROCESS_PY);
|
|
18454
18602
|
py.runPython(NETWORK_PY);
|
|
18603
|
+
py.runPython(ASYNCIO_PY);
|
|
18455
18604
|
}
|
|
18456
18605
|
function bindProgram(py, binding) {
|
|
18457
18606
|
py.__sbxSlot.current = binding;
|
|
18458
18607
|
const { ctx, stdin } = binding;
|
|
18459
|
-
const
|
|
18608
|
+
const decoder9 = new TextDecoder();
|
|
18460
18609
|
py.setStdout({
|
|
18461
|
-
write: (buffer) => (ctx.write(
|
|
18610
|
+
write: (buffer) => (ctx.write(decoder9.decode(buffer)), buffer.length)
|
|
18462
18611
|
});
|
|
18463
18612
|
py.setStderr({
|
|
18464
|
-
write: (buffer) => (ctx.stderr.write(
|
|
18613
|
+
write: (buffer) => (ctx.stderr.write(decoder9.decode(buffer)), buffer.length)
|
|
18465
18614
|
});
|
|
18466
18615
|
py.setStdin({
|
|
18467
18616
|
read: (buffer) => {
|
|
@@ -18725,6 +18874,67 @@ async function runCPythonRepl(ctx) {
|
|
|
18725
18874
|
}
|
|
18726
18875
|
return 0;
|
|
18727
18876
|
}
|
|
18877
|
+
var PIP_PY = `
|
|
18878
|
+
import json, re
|
|
18879
|
+
|
|
18880
|
+
import micropip
|
|
18881
|
+
|
|
18882
|
+
_MISSING = re.compile(r"Can't find a pure Python 3 wheel for '([^']+)'")
|
|
18883
|
+
_EXTRAS = re.compile(r"^\\s*([A-Za-z0-9._-]+)\\s*\\[([^\\]]+)\\](.*)$")
|
|
18884
|
+
|
|
18885
|
+
|
|
18886
|
+
def _name_of(spec):
|
|
18887
|
+
return re.split(r"[\\[<>=!~;\\s]", spec.strip(), 1)[0]
|
|
18888
|
+
|
|
18889
|
+
|
|
18890
|
+
def _installed_version(name):
|
|
18891
|
+
try:
|
|
18892
|
+
found = micropip.list()[name.replace("_", "-").lower()]
|
|
18893
|
+
return getattr(found, "version", None)
|
|
18894
|
+
except Exception:
|
|
18895
|
+
return None
|
|
18896
|
+
|
|
18897
|
+
|
|
18898
|
+
async def _install_one(spec):
|
|
18899
|
+
"""Install one requirement, retrying without extras if they are impossible."""
|
|
18900
|
+
attempt = spec
|
|
18901
|
+
dropped = None
|
|
18902
|
+
while True:
|
|
18903
|
+
try:
|
|
18904
|
+
await micropip.install(attempt)
|
|
18905
|
+
except ValueError as error:
|
|
18906
|
+
message = str(error)
|
|
18907
|
+
missing = _MISSING.findall(message)
|
|
18908
|
+
extras = _EXTRAS.match(attempt)
|
|
18909
|
+
if missing and extras and dropped is None:
|
|
18910
|
+
dropped = [e.strip() for e in extras.group(2).split(",")]
|
|
18911
|
+
attempt = extras.group(1) + extras.group(3)
|
|
18912
|
+
continue
|
|
18913
|
+
return {
|
|
18914
|
+
"spec": spec, "ok": False, "missing": missing,
|
|
18915
|
+
"reason": message.strip().split("\\n")[0],
|
|
18916
|
+
}
|
|
18917
|
+
except Exception as error:
|
|
18918
|
+
return {"spec": spec, "ok": False, "missing": [], "reason": str(error).strip().split("\\n")[0]}
|
|
18919
|
+
|
|
18920
|
+
name = _name_of(attempt)
|
|
18921
|
+
return {
|
|
18922
|
+
"spec": spec, "ok": True, "name": name,
|
|
18923
|
+
"version": _installed_version(name), "dropped": dropped,
|
|
18924
|
+
}
|
|
18925
|
+
|
|
18926
|
+
|
|
18927
|
+
async def __sbx_pip(specs_json):
|
|
18928
|
+
results = []
|
|
18929
|
+
for spec in json.loads(specs_json):
|
|
18930
|
+
try:
|
|
18931
|
+
results.append(await _install_one(spec))
|
|
18932
|
+
except Exception as error:
|
|
18933
|
+
results.append({"spec": spec, "ok": False, "missing": [], "reason": str(error)})
|
|
18934
|
+
return json.dumps(results)
|
|
18935
|
+
|
|
18936
|
+
__sbx_pip
|
|
18937
|
+
`;
|
|
18728
18938
|
var micropip = defineCommand({
|
|
18729
18939
|
name: "micropip",
|
|
18730
18940
|
path: "/usr/bin/micropip",
|
|
@@ -18762,8 +18972,7 @@ var micropip = defineCommand({
|
|
|
18762
18972
|
ctx.stderr.write("pip: no packages specified\n");
|
|
18763
18973
|
return 1;
|
|
18764
18974
|
}
|
|
18765
|
-
|
|
18766
|
-
if (!ctx.kernel.net.outboundAllowed(index)) {
|
|
18975
|
+
if (!ctx.kernel.net.outboundAllowed("https://pypi.org")) {
|
|
18767
18976
|
ctx.stderr.write(
|
|
18768
18977
|
"pip: outbound network access is disabled for this container (enable with network: { allowOutbound: true })\n"
|
|
18769
18978
|
);
|
|
@@ -18776,22 +18985,42 @@ var micropip = defineCommand({
|
|
|
18776
18985
|
ctx.stderr.write("pip: Pyodide is unavailable in this host\n");
|
|
18777
18986
|
return 127;
|
|
18778
18987
|
}
|
|
18988
|
+
let outcomes;
|
|
18779
18989
|
try {
|
|
18780
18990
|
await py.loadPackage("micropip");
|
|
18781
|
-
const
|
|
18782
|
-
|
|
18783
|
-
|
|
18784
|
-
await micropipModule.install(name);
|
|
18785
|
-
ctx.line(`Successfully installed ${name}`);
|
|
18786
|
-
}
|
|
18787
|
-
return 0;
|
|
18991
|
+
for (const name of packages) ctx.line(`Collecting ${name}`);
|
|
18992
|
+
const install2 = py.runPython(PIP_PY);
|
|
18993
|
+
outcomes = JSON.parse(String(await install2(JSON.stringify(packages))));
|
|
18788
18994
|
} catch (error) {
|
|
18789
18995
|
ctx.stderr.write(
|
|
18790
|
-
`
|
|
18996
|
+
`pip: installation failed: ${error instanceof Error ? error.message : String(error)}
|
|
18791
18997
|
`
|
|
18792
18998
|
);
|
|
18793
18999
|
return 1;
|
|
18794
19000
|
}
|
|
19001
|
+
const installed2 = [];
|
|
19002
|
+
for (const outcome of outcomes) {
|
|
19003
|
+
if (outcome.ok) {
|
|
19004
|
+
installed2.push(
|
|
19005
|
+
outcome.version ? `${outcome.name}-${outcome.version}` : String(outcome.name)
|
|
19006
|
+
);
|
|
19007
|
+
if (outcome.dropped?.length) {
|
|
19008
|
+
ctx.stderr.write(
|
|
19009
|
+
` WARNING: ${outcome.name}: the ${outcome.dropped.map((extra) => `'${extra}'`).join(", ")} extra needs native code with no WebAssembly build; installed ${outcome.name} without it
|
|
19010
|
+
`
|
|
19011
|
+
);
|
|
19012
|
+
}
|
|
19013
|
+
continue;
|
|
19014
|
+
}
|
|
19015
|
+
const missing = outcome.missing ?? [];
|
|
19016
|
+
ctx.stderr.write(
|
|
19017
|
+
missing.length > 0 ? `ERROR: could not install ${outcome.spec}: no WebAssembly build exists for ${missing.map((name) => name.split(/[<>=!~;\s]/)[0]).join(", ")}
|
|
19018
|
+
` : `ERROR: could not install ${outcome.spec}: ${outcome.reason ?? "unknown error"}
|
|
19019
|
+
`
|
|
19020
|
+
);
|
|
19021
|
+
}
|
|
19022
|
+
if (installed2.length > 0) ctx.line(`Successfully installed ${installed2.join(" ")}`);
|
|
19023
|
+
return outcomes.every((outcome) => outcome.ok) ? 0 : 1;
|
|
18795
19024
|
}
|
|
18796
19025
|
});
|
|
18797
19026
|
|
|
@@ -18995,6 +19224,1001 @@ var ffprobe = defineCommand({
|
|
|
18995
19224
|
function ffmpegCommands() {
|
|
18996
19225
|
return [ffmpeg, ffprobe];
|
|
18997
19226
|
}
|
|
19227
|
+
|
|
19228
|
+
// src/wasi/host.ts
|
|
19229
|
+
init_path();
|
|
19230
|
+
init_errno();
|
|
19231
|
+
init_mode();
|
|
19232
|
+
var WasiExit = class extends Error {
|
|
19233
|
+
constructor(code) {
|
|
19234
|
+
super(`wasi exit ${code}`);
|
|
19235
|
+
this.code = code;
|
|
19236
|
+
this.name = "WasiExit";
|
|
19237
|
+
}
|
|
19238
|
+
code;
|
|
19239
|
+
};
|
|
19240
|
+
var encoder6 = new TextEncoder();
|
|
19241
|
+
var decoder7 = new TextDecoder();
|
|
19242
|
+
var FIRST_PREOPEN = 3;
|
|
19243
|
+
var WasiHost = class {
|
|
19244
|
+
memory;
|
|
19245
|
+
fds = /* @__PURE__ */ new Map();
|
|
19246
|
+
nextFd = FIRST_PREOPEN;
|
|
19247
|
+
opts;
|
|
19248
|
+
startTime;
|
|
19249
|
+
/** Set once `proc_exit` has run, so the runner reports the guest's code. */
|
|
19250
|
+
exitCode = null;
|
|
19251
|
+
constructor(options) {
|
|
19252
|
+
this.opts = {
|
|
19253
|
+
now: Date.now,
|
|
19254
|
+
hrtime: defaultHrtime,
|
|
19255
|
+
random: defaultRandom,
|
|
19256
|
+
sleep: defaultSleep,
|
|
19257
|
+
aborted: () => false,
|
|
19258
|
+
...options
|
|
19259
|
+
};
|
|
19260
|
+
this.startTime = this.opts.hrtime();
|
|
19261
|
+
this.fds.set(0, { kind: "stdin" });
|
|
19262
|
+
this.fds.set(1, { kind: "stdout", stream: options.stdout });
|
|
19263
|
+
this.fds.set(2, { kind: "stderr", stream: options.stderr });
|
|
19264
|
+
const preopens = options.preopens ?? { "/": "/" };
|
|
19265
|
+
for (const [name, path] of Object.entries(preopens)) {
|
|
19266
|
+
const abs = resolve("/", path);
|
|
19267
|
+
this.fds.set(this.nextFd++, {
|
|
19268
|
+
kind: "dir",
|
|
19269
|
+
path: abs,
|
|
19270
|
+
root: abs,
|
|
19271
|
+
preopen: name,
|
|
19272
|
+
rights: RIGHTS_ALL
|
|
19273
|
+
});
|
|
19274
|
+
}
|
|
19275
|
+
}
|
|
19276
|
+
/** Attach the instance's memory. Called before `_start`. */
|
|
19277
|
+
bind(instance) {
|
|
19278
|
+
const exported = instance.exports.memory;
|
|
19279
|
+
if (!(exported instanceof WebAssembly.Memory)) {
|
|
19280
|
+
throw new Error("wasi: the module does not export a linear memory");
|
|
19281
|
+
}
|
|
19282
|
+
this.memory = exported;
|
|
19283
|
+
}
|
|
19284
|
+
/** Flush every buffered write. Called by the runner when the guest ends. */
|
|
19285
|
+
flushAll() {
|
|
19286
|
+
for (const fd of this.fds.values()) if (fd.kind === "file") this.writeBack(fd);
|
|
19287
|
+
}
|
|
19288
|
+
/**
|
|
19289
|
+
* `wasi_snapshot_preview1`.
|
|
19290
|
+
*
|
|
19291
|
+
* Every entry returns an errno rather than throwing: a JavaScript exception
|
|
19292
|
+
* crossing back into WebAssembly traps the instance, which turns a missing
|
|
19293
|
+
* file into an unrecoverable crash instead of the `ENOENT` the guest is
|
|
19294
|
+
* written to handle. `guard` is what enforces that.
|
|
19295
|
+
*/
|
|
19296
|
+
get wasiImport() {
|
|
19297
|
+
const g = (fn) => ((...args) => this.guard(() => fn.apply(this, args)));
|
|
19298
|
+
return {
|
|
19299
|
+
args_get: g(this.args_get),
|
|
19300
|
+
args_sizes_get: g(this.args_sizes_get),
|
|
19301
|
+
environ_get: g(this.environ_get),
|
|
19302
|
+
environ_sizes_get: g(this.environ_sizes_get),
|
|
19303
|
+
clock_res_get: g(this.clock_res_get),
|
|
19304
|
+
clock_time_get: g(this.clock_time_get),
|
|
19305
|
+
fd_advise: g(() => WASI_ESUCCESS),
|
|
19306
|
+
fd_allocate: g(this.fd_allocate),
|
|
19307
|
+
fd_close: g(this.fd_close),
|
|
19308
|
+
fd_datasync: g(this.fd_sync),
|
|
19309
|
+
fd_fdstat_get: g(this.fd_fdstat_get),
|
|
19310
|
+
fd_fdstat_set_flags: g(this.fd_fdstat_set_flags),
|
|
19311
|
+
fd_fdstat_set_rights: g(() => WASI_ESUCCESS),
|
|
19312
|
+
fd_filestat_get: g(this.fd_filestat_get),
|
|
19313
|
+
fd_filestat_set_size: g(this.fd_filestat_set_size),
|
|
19314
|
+
fd_filestat_set_times: g(this.fd_filestat_set_times),
|
|
19315
|
+
fd_pread: g(this.fd_pread),
|
|
19316
|
+
fd_prestat_get: g(this.fd_prestat_get),
|
|
19317
|
+
fd_prestat_dir_name: g(this.fd_prestat_dir_name),
|
|
19318
|
+
fd_pwrite: g(this.fd_pwrite),
|
|
19319
|
+
fd_read: g(this.fd_read),
|
|
19320
|
+
fd_readdir: g(this.fd_readdir),
|
|
19321
|
+
fd_renumber: g(this.fd_renumber),
|
|
19322
|
+
fd_seek: g(this.fd_seek),
|
|
19323
|
+
fd_sync: g(this.fd_sync),
|
|
19324
|
+
fd_tell: g(this.fd_tell),
|
|
19325
|
+
fd_write: g(this.fd_write),
|
|
19326
|
+
path_create_directory: g(this.path_create_directory),
|
|
19327
|
+
path_filestat_get: g(this.path_filestat_get),
|
|
19328
|
+
path_filestat_set_times: g(this.path_filestat_set_times),
|
|
19329
|
+
path_link: g(this.path_link),
|
|
19330
|
+
path_open: g(this.path_open),
|
|
19331
|
+
path_readlink: g(this.path_readlink),
|
|
19332
|
+
path_remove_directory: g(this.path_remove_directory),
|
|
19333
|
+
path_rename: g(this.path_rename),
|
|
19334
|
+
path_symlink: g(this.path_symlink),
|
|
19335
|
+
path_unlink_file: g(this.path_unlink_file),
|
|
19336
|
+
poll_oneoff: g(this.poll_oneoff),
|
|
19337
|
+
proc_exit: ((code) => {
|
|
19338
|
+
this.exitCode = code;
|
|
19339
|
+
throw new WasiExit(code);
|
|
19340
|
+
}),
|
|
19341
|
+
proc_raise: g(() => WASI_ENOSYS),
|
|
19342
|
+
random_get: g(this.random_get),
|
|
19343
|
+
sched_yield: g(() => WASI_ESUCCESS),
|
|
19344
|
+
/* Sockets are the one part of WASI this container cannot honour: its
|
|
19345
|
+
* network is an HTTP router, not a packet path. Saying so with ENOTSUP
|
|
19346
|
+
* lets a guest fall back, where a trap would just kill it. */
|
|
19347
|
+
sock_accept: g(() => WASI_ENOTSUP),
|
|
19348
|
+
sock_recv: g(() => WASI_ENOTSUP),
|
|
19349
|
+
sock_send: g(() => WASI_ENOTSUP),
|
|
19350
|
+
sock_shutdown: g(() => WASI_ENOTSUP)
|
|
19351
|
+
};
|
|
19352
|
+
}
|
|
19353
|
+
/**
|
|
19354
|
+
* `wasi_unstable`, the preview0 name older toolchains still emit.
|
|
19355
|
+
*
|
|
19356
|
+
* Identical but for `fd_seek`, whose `whence` values were reordered before
|
|
19357
|
+
* preview1 was frozen. Aliasing the table without this correction is a
|
|
19358
|
+
* popular bug: every seek in an old binary lands somewhere plausible and
|
|
19359
|
+
* wrong.
|
|
19360
|
+
*/
|
|
19361
|
+
get wasiUnstableImport() {
|
|
19362
|
+
const preview0Whence = [WHENCE_CUR, WHENCE_END, WHENCE_SET];
|
|
19363
|
+
return {
|
|
19364
|
+
...this.wasiImport,
|
|
19365
|
+
fd_seek: ((fd, offset, whence, out) => this.guard(
|
|
19366
|
+
() => this.fd_seek(fd, offset, preview0Whence[whence] ?? WHENCE_SET, out)
|
|
19367
|
+
))
|
|
19368
|
+
};
|
|
19369
|
+
}
|
|
19370
|
+
// ── memory access ────────────────────────────────────────────────────────
|
|
19371
|
+
//
|
|
19372
|
+
// Views are built per call rather than cached: `memory.grow` detaches every
|
|
19373
|
+
// existing view, so a cached one turns into a silent zero-length buffer the
|
|
19374
|
+
// first time the guest's allocator asks for another page.
|
|
19375
|
+
get view() {
|
|
19376
|
+
if (!this.memory) throw new Error("wasi: memory is not bound");
|
|
19377
|
+
return new DataView(this.memory.buffer);
|
|
19378
|
+
}
|
|
19379
|
+
get bytes() {
|
|
19380
|
+
if (!this.memory) throw new Error("wasi: memory is not bound");
|
|
19381
|
+
return new Uint8Array(this.memory.buffer);
|
|
19382
|
+
}
|
|
19383
|
+
readString(ptr, len) {
|
|
19384
|
+
return decoder7.decode(this.bytes.subarray(ptr, ptr + len));
|
|
19385
|
+
}
|
|
19386
|
+
/** The scatter/gather list `fd_read` and `fd_write` are given. */
|
|
19387
|
+
iovecs(ptr, count) {
|
|
19388
|
+
const view = this.view;
|
|
19389
|
+
const out = [];
|
|
19390
|
+
for (let i = 0; i < count; i++) {
|
|
19391
|
+
const base2 = ptr + i * 8;
|
|
19392
|
+
out.push({ offset: view.getUint32(base2, true), length: view.getUint32(base2 + 4, true) });
|
|
19393
|
+
}
|
|
19394
|
+
return out;
|
|
19395
|
+
}
|
|
19396
|
+
/**
|
|
19397
|
+
* Turn anything thrown inside a syscall into an errno.
|
|
19398
|
+
*
|
|
19399
|
+
* `WasiExit` is re-thrown on purpose: it is the guest unwinding its own
|
|
19400
|
+
* stack, not a failure to be reported through a return value.
|
|
19401
|
+
*/
|
|
19402
|
+
guard(fn) {
|
|
19403
|
+
try {
|
|
19404
|
+
return fn();
|
|
19405
|
+
} catch (error) {
|
|
19406
|
+
if (error instanceof WasiExit) throw error;
|
|
19407
|
+
return errnoOf(error);
|
|
19408
|
+
}
|
|
19409
|
+
}
|
|
19410
|
+
// ── descriptors ──────────────────────────────────────────────────────────
|
|
19411
|
+
get(fd) {
|
|
19412
|
+
const found = this.fds.get(fd);
|
|
19413
|
+
if (!found) throw new WasiError(WASI_EBADF);
|
|
19414
|
+
return found;
|
|
19415
|
+
}
|
|
19416
|
+
dir(fd) {
|
|
19417
|
+
const found = this.get(fd);
|
|
19418
|
+
if (found.kind !== "dir") throw new WasiError(WASI_ENOTDIR);
|
|
19419
|
+
return found;
|
|
19420
|
+
}
|
|
19421
|
+
file(fd) {
|
|
19422
|
+
const found = this.get(fd);
|
|
19423
|
+
if (found.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19424
|
+
return found;
|
|
19425
|
+
}
|
|
19426
|
+
/**
|
|
19427
|
+
* Resolve a guest path against a directory descriptor.
|
|
19428
|
+
*
|
|
19429
|
+
* The confinement check is the one place preopens are enforced: a path that
|
|
19430
|
+
* climbs out of the directory the descriptor was derived from is
|
|
19431
|
+
* `ENOTCAPABLE`, which is exactly the error a capability-oriented guest
|
|
19432
|
+
* expects and knows how to report.
|
|
19433
|
+
*/
|
|
19434
|
+
resolveAt(dirfd, path) {
|
|
19435
|
+
const dir3 = this.dir(dirfd);
|
|
19436
|
+
if (path === "") throw new WasiError(WASI_ENOENT);
|
|
19437
|
+
const abs = isAbsolute(path) ? resolve("/", path) : resolve(dir3.path, path);
|
|
19438
|
+
const root = dir3.root;
|
|
19439
|
+
if (root !== "/" && abs !== root && !abs.startsWith(root.endsWith("/") ? root : `${root}/`)) {
|
|
19440
|
+
throw new WasiError(WASI_ENOTCAPABLE);
|
|
19441
|
+
}
|
|
19442
|
+
return { abs, root };
|
|
19443
|
+
}
|
|
19444
|
+
allocate(descriptor) {
|
|
19445
|
+
const fd = this.nextFd++;
|
|
19446
|
+
this.fds.set(fd, descriptor);
|
|
19447
|
+
return fd;
|
|
19448
|
+
}
|
|
19449
|
+
/**
|
|
19450
|
+
* Push a buffered file back to the VFS.
|
|
19451
|
+
*
|
|
19452
|
+
* Called before every path-based call as well as on close, so a guest that
|
|
19453
|
+
* writes a file and then stats or reopens it by name sees what it wrote —
|
|
19454
|
+
* the alternative is a stale read that looks like data loss.
|
|
19455
|
+
*/
|
|
19456
|
+
writeBack(fd) {
|
|
19457
|
+
if (!fd.dirty) return;
|
|
19458
|
+
this.opts.vfs.writeFile(fd.path, fd.data.subarray(0, fd.size), { cred: this.opts.cred });
|
|
19459
|
+
fd.dirty = false;
|
|
19460
|
+
}
|
|
19461
|
+
syncPaths() {
|
|
19462
|
+
for (const fd of this.fds.values()) if (fd.kind === "file" && fd.dirty) this.writeBack(fd);
|
|
19463
|
+
}
|
|
19464
|
+
// ── args and environment ─────────────────────────────────────────────────
|
|
19465
|
+
args_get(argvPtr, bufPtr) {
|
|
19466
|
+
return this.writeStringVector(this.opts.argv, argvPtr, bufPtr);
|
|
19467
|
+
}
|
|
19468
|
+
args_sizes_get(countPtr, sizePtr) {
|
|
19469
|
+
return this.writeVectorSizes(this.opts.argv, countPtr, sizePtr);
|
|
19470
|
+
}
|
|
19471
|
+
get envStrings() {
|
|
19472
|
+
return Object.entries(this.opts.env).map(([key, value]) => `${key}=${value}`);
|
|
19473
|
+
}
|
|
19474
|
+
environ_get(envPtr, bufPtr) {
|
|
19475
|
+
return this.writeStringVector(this.envStrings, envPtr, bufPtr);
|
|
19476
|
+
}
|
|
19477
|
+
environ_sizes_get(countPtr, sizePtr) {
|
|
19478
|
+
return this.writeVectorSizes(this.envStrings, countPtr, sizePtr);
|
|
19479
|
+
}
|
|
19480
|
+
writeStringVector(values, pointersPtr, bufPtr) {
|
|
19481
|
+
const view = this.view;
|
|
19482
|
+
const memory = this.bytes;
|
|
19483
|
+
let cursor = bufPtr;
|
|
19484
|
+
values.forEach((value, index) => {
|
|
19485
|
+
view.setUint32(pointersPtr + index * 4, cursor, true);
|
|
19486
|
+
const encoded = encoder6.encode(`${value}\0`);
|
|
19487
|
+
memory.set(encoded, cursor);
|
|
19488
|
+
cursor += encoded.length;
|
|
19489
|
+
});
|
|
19490
|
+
return WASI_ESUCCESS;
|
|
19491
|
+
}
|
|
19492
|
+
writeVectorSizes(values, countPtr, sizePtr) {
|
|
19493
|
+
const view = this.view;
|
|
19494
|
+
view.setUint32(countPtr, values.length, true);
|
|
19495
|
+
view.setUint32(
|
|
19496
|
+
sizePtr,
|
|
19497
|
+
values.reduce((total, value) => total + encoder6.encode(value).length + 1, 0),
|
|
19498
|
+
true
|
|
19499
|
+
);
|
|
19500
|
+
return WASI_ESUCCESS;
|
|
19501
|
+
}
|
|
19502
|
+
// ── clocks and randomness ────────────────────────────────────────────────
|
|
19503
|
+
clock_res_get(_id, out) {
|
|
19504
|
+
this.view.setBigUint64(out, 1000000n, true);
|
|
19505
|
+
return WASI_ESUCCESS;
|
|
19506
|
+
}
|
|
19507
|
+
clock_time_get(id, _precision, out) {
|
|
19508
|
+
this.view.setBigUint64(out, this.clockNow(id), true);
|
|
19509
|
+
return WASI_ESUCCESS;
|
|
19510
|
+
}
|
|
19511
|
+
clockNow(id) {
|
|
19512
|
+
if (id === 0) return BigInt(Math.round(this.opts.now())) * 1000000n;
|
|
19513
|
+
return this.opts.hrtime() - this.startTime;
|
|
19514
|
+
}
|
|
19515
|
+
random_get(ptr, len) {
|
|
19516
|
+
const into = new Uint8Array(len);
|
|
19517
|
+
this.opts.random(into);
|
|
19518
|
+
this.bytes.set(into, ptr);
|
|
19519
|
+
return WASI_ESUCCESS;
|
|
19520
|
+
}
|
|
19521
|
+
// ── reading and writing ──────────────────────────────────────────────────
|
|
19522
|
+
fd_read(fd, iovsPtr, iovsLen, out) {
|
|
19523
|
+
const descriptor = this.get(fd);
|
|
19524
|
+
const iovs = this.iovecs(iovsPtr, iovsLen);
|
|
19525
|
+
let total = 0;
|
|
19526
|
+
if (descriptor.kind === "stdin") {
|
|
19527
|
+
for (const iov of iovs) {
|
|
19528
|
+
if (iov.length === 0) continue;
|
|
19529
|
+
const chunk = this.opts.stdin.read(iov.length);
|
|
19530
|
+
if (chunk.length === 0) break;
|
|
19531
|
+
this.bytes.set(chunk, iov.offset);
|
|
19532
|
+
total += chunk.length;
|
|
19533
|
+
if (chunk.length < iov.length) break;
|
|
19534
|
+
}
|
|
19535
|
+
this.view.setUint32(out, total, true);
|
|
19536
|
+
return WASI_ESUCCESS;
|
|
19537
|
+
}
|
|
19538
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19539
|
+
if ((descriptor.rights & RIGHT_FD_READ) === 0n) throw new WasiError(WASI_EACCES);
|
|
19540
|
+
for (const iov of iovs) {
|
|
19541
|
+
const end = Math.min(descriptor.pos + iov.length, descriptor.size);
|
|
19542
|
+
const chunk = descriptor.data.subarray(descriptor.pos, Math.max(end, descriptor.pos));
|
|
19543
|
+
if (chunk.length === 0) break;
|
|
19544
|
+
this.bytes.set(chunk, iov.offset);
|
|
19545
|
+
descriptor.pos += chunk.length;
|
|
19546
|
+
total += chunk.length;
|
|
19547
|
+
if (chunk.length < iov.length) break;
|
|
19548
|
+
}
|
|
19549
|
+
this.view.setUint32(out, total, true);
|
|
19550
|
+
return WASI_ESUCCESS;
|
|
19551
|
+
}
|
|
19552
|
+
fd_pread(fd, iovsPtr, iovsLen, offset, out) {
|
|
19553
|
+
const descriptor = this.get(fd);
|
|
19554
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19555
|
+
const saved = descriptor.pos;
|
|
19556
|
+
descriptor.pos = Number(offset);
|
|
19557
|
+
try {
|
|
19558
|
+
return this.fd_read(fd, iovsPtr, iovsLen, out);
|
|
19559
|
+
} finally {
|
|
19560
|
+
descriptor.pos = saved;
|
|
19561
|
+
}
|
|
19562
|
+
}
|
|
19563
|
+
fd_write(fd, iovsPtr, iovsLen, out) {
|
|
19564
|
+
const descriptor = this.get(fd);
|
|
19565
|
+
const iovs = this.iovecs(iovsPtr, iovsLen);
|
|
19566
|
+
let total = 0;
|
|
19567
|
+
if (descriptor.kind === "stdout" || descriptor.kind === "stderr") {
|
|
19568
|
+
for (const iov of iovs) {
|
|
19569
|
+
const chunk = this.bytes.slice(iov.offset, iov.offset + iov.length);
|
|
19570
|
+
try {
|
|
19571
|
+
descriptor.stream.write(chunk);
|
|
19572
|
+
} catch (error) {
|
|
19573
|
+
if (isSysError(error) && error.code === "EPIPE") throw new WasiError(WASI_EPIPE);
|
|
19574
|
+
throw error;
|
|
19575
|
+
}
|
|
19576
|
+
total += chunk.length;
|
|
19577
|
+
}
|
|
19578
|
+
this.view.setUint32(out, total, true);
|
|
19579
|
+
return WASI_ESUCCESS;
|
|
19580
|
+
}
|
|
19581
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19582
|
+
if ((descriptor.rights & RIGHT_FD_WRITE) === 0n) throw new WasiError(WASI_EACCES);
|
|
19583
|
+
if (descriptor.append) descriptor.pos = descriptor.size;
|
|
19584
|
+
for (const iov of iovs) {
|
|
19585
|
+
const chunk = this.bytes.subarray(iov.offset, iov.offset + iov.length);
|
|
19586
|
+
reserve(descriptor, descriptor.pos + chunk.length);
|
|
19587
|
+
descriptor.data.set(chunk, descriptor.pos);
|
|
19588
|
+
descriptor.pos += chunk.length;
|
|
19589
|
+
descriptor.size = Math.max(descriptor.size, descriptor.pos);
|
|
19590
|
+
total += chunk.length;
|
|
19591
|
+
}
|
|
19592
|
+
descriptor.dirty = true;
|
|
19593
|
+
this.view.setUint32(out, total, true);
|
|
19594
|
+
return WASI_ESUCCESS;
|
|
19595
|
+
}
|
|
19596
|
+
fd_pwrite(fd, iovsPtr, iovsLen, offset, out) {
|
|
19597
|
+
const descriptor = this.get(fd);
|
|
19598
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19599
|
+
const saved = descriptor.pos;
|
|
19600
|
+
const appended = descriptor.append;
|
|
19601
|
+
descriptor.pos = Number(offset);
|
|
19602
|
+
descriptor.append = false;
|
|
19603
|
+
try {
|
|
19604
|
+
return this.fd_write(fd, iovsPtr, iovsLen, out);
|
|
19605
|
+
} finally {
|
|
19606
|
+
descriptor.pos = saved;
|
|
19607
|
+
descriptor.append = appended;
|
|
19608
|
+
}
|
|
19609
|
+
}
|
|
19610
|
+
fd_seek(fd, offset, whence, out) {
|
|
19611
|
+
const descriptor = this.get(fd);
|
|
19612
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19613
|
+
if ((descriptor.rights & RIGHT_FD_SEEK) === 0n) throw new WasiError(WASI_EACCES);
|
|
19614
|
+
const base2 = whence === WHENCE_SET ? 0 : whence === WHENCE_CUR ? descriptor.pos : descriptor.size;
|
|
19615
|
+
const next = base2 + Number(offset);
|
|
19616
|
+
if (next < 0) throw new WasiError(WASI_EINVAL);
|
|
19617
|
+
descriptor.pos = next;
|
|
19618
|
+
this.view.setBigUint64(out, BigInt(next), true);
|
|
19619
|
+
return WASI_ESUCCESS;
|
|
19620
|
+
}
|
|
19621
|
+
fd_tell(fd, out) {
|
|
19622
|
+
this.view.setBigUint64(out, BigInt(this.file(fd).pos), true);
|
|
19623
|
+
return WASI_ESUCCESS;
|
|
19624
|
+
}
|
|
19625
|
+
fd_close(fd) {
|
|
19626
|
+
const descriptor = this.get(fd);
|
|
19627
|
+
if (descriptor.kind === "file") this.writeBack(descriptor);
|
|
19628
|
+
this.fds.delete(fd);
|
|
19629
|
+
return WASI_ESUCCESS;
|
|
19630
|
+
}
|
|
19631
|
+
fd_sync(fd) {
|
|
19632
|
+
const descriptor = this.get(fd);
|
|
19633
|
+
if (descriptor.kind === "file") this.writeBack(descriptor);
|
|
19634
|
+
return WASI_ESUCCESS;
|
|
19635
|
+
}
|
|
19636
|
+
fd_renumber(from, to) {
|
|
19637
|
+
const descriptor = this.get(from);
|
|
19638
|
+
const replaced = this.fds.get(to);
|
|
19639
|
+
if (replaced?.kind === "file") this.writeBack(replaced);
|
|
19640
|
+
this.fds.set(to, descriptor);
|
|
19641
|
+
this.fds.delete(from);
|
|
19642
|
+
return WASI_ESUCCESS;
|
|
19643
|
+
}
|
|
19644
|
+
fd_allocate(fd, offset, len) {
|
|
19645
|
+
const descriptor = this.file(fd);
|
|
19646
|
+
const size = Number(offset + len);
|
|
19647
|
+
if (descriptor.size < size) {
|
|
19648
|
+
reserve(descriptor, size);
|
|
19649
|
+
descriptor.data.fill(0, descriptor.size, size);
|
|
19650
|
+
descriptor.size = size;
|
|
19651
|
+
descriptor.dirty = true;
|
|
19652
|
+
}
|
|
19653
|
+
return WASI_ESUCCESS;
|
|
19654
|
+
}
|
|
19655
|
+
fd_filestat_set_size(fd, size) {
|
|
19656
|
+
const descriptor = this.file(fd);
|
|
19657
|
+
const length = Number(size);
|
|
19658
|
+
reserve(descriptor, length);
|
|
19659
|
+
if (length > descriptor.size) descriptor.data.fill(0, descriptor.size, length);
|
|
19660
|
+
descriptor.size = length;
|
|
19661
|
+
if (descriptor.pos > length) descriptor.pos = length;
|
|
19662
|
+
descriptor.dirty = true;
|
|
19663
|
+
return WASI_ESUCCESS;
|
|
19664
|
+
}
|
|
19665
|
+
// ── metadata ─────────────────────────────────────────────────────────────
|
|
19666
|
+
fd_fdstat_get(fd, out) {
|
|
19667
|
+
const descriptor = this.get(fd);
|
|
19668
|
+
const view = this.view;
|
|
19669
|
+
const [filetype, flags, rights] = (() => {
|
|
19670
|
+
switch (descriptor.kind) {
|
|
19671
|
+
case "dir":
|
|
19672
|
+
return [FILETYPE_DIRECTORY, 0, descriptor.rights];
|
|
19673
|
+
case "file":
|
|
19674
|
+
return [FILETYPE_REGULAR_FILE, descriptor.flags, descriptor.rights];
|
|
19675
|
+
default:
|
|
19676
|
+
return [FILETYPE_CHARACTER_DEVICE, 0, RIGHTS_ALL];
|
|
19677
|
+
}
|
|
19678
|
+
})();
|
|
19679
|
+
zero(this.bytes, out, SIZEOF_FDSTAT);
|
|
19680
|
+
view.setUint8(out, filetype);
|
|
19681
|
+
view.setUint16(out + 2, flags, true);
|
|
19682
|
+
view.setBigUint64(out + 8, rights, true);
|
|
19683
|
+
view.setBigUint64(out + 16, rights, true);
|
|
19684
|
+
return WASI_ESUCCESS;
|
|
19685
|
+
}
|
|
19686
|
+
fd_fdstat_set_flags(fd, flags) {
|
|
19687
|
+
const descriptor = this.file(fd);
|
|
19688
|
+
descriptor.flags = flags;
|
|
19689
|
+
descriptor.append = (flags & FD_APPEND) !== 0;
|
|
19690
|
+
return WASI_ESUCCESS;
|
|
19691
|
+
}
|
|
19692
|
+
fd_filestat_get(fd, out) {
|
|
19693
|
+
const descriptor = this.get(fd);
|
|
19694
|
+
if (descriptor.kind === "file" || descriptor.kind === "dir") {
|
|
19695
|
+
this.syncPaths();
|
|
19696
|
+
this.writeFilestat(out, this.opts.vfs.stat(descriptor.path, { cred: this.opts.cred }));
|
|
19697
|
+
return WASI_ESUCCESS;
|
|
19698
|
+
}
|
|
19699
|
+
zero(this.bytes, out, SIZEOF_FILESTAT);
|
|
19700
|
+
this.view.setUint8(out + 16, FILETYPE_CHARACTER_DEVICE);
|
|
19701
|
+
return WASI_ESUCCESS;
|
|
19702
|
+
}
|
|
19703
|
+
fd_filestat_set_times(fd, atim, mtim, flags) {
|
|
19704
|
+
const descriptor = this.get(fd);
|
|
19705
|
+
if (descriptor.kind !== "file" && descriptor.kind !== "dir") return WASI_ESUCCESS;
|
|
19706
|
+
return this.setTimes(descriptor.path, atim, mtim, flags);
|
|
19707
|
+
}
|
|
19708
|
+
path_filestat_get(dirfd, lookupFlags, pathPtr, pathLen, out) {
|
|
19709
|
+
this.syncPaths();
|
|
19710
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19711
|
+
const follow = (lookupFlags & LOOKUP_SYMLINK_FOLLOW) !== 0;
|
|
19712
|
+
this.writeFilestat(
|
|
19713
|
+
out,
|
|
19714
|
+
follow ? this.opts.vfs.stat(abs, { cred: this.opts.cred }) : this.opts.vfs.lstat(abs)
|
|
19715
|
+
);
|
|
19716
|
+
return WASI_ESUCCESS;
|
|
19717
|
+
}
|
|
19718
|
+
path_filestat_set_times(dirfd, _lookupFlags, pathPtr, pathLen, atim, mtim, flags) {
|
|
19719
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19720
|
+
return this.setTimes(abs, atim, mtim, flags);
|
|
19721
|
+
}
|
|
19722
|
+
setTimes(path, atim, mtim, flags) {
|
|
19723
|
+
const current = this.opts.vfs.stat(path, { cred: this.opts.cred });
|
|
19724
|
+
const now = this.opts.now();
|
|
19725
|
+
const atime = flags & FST_ATIM_NOW ? now : flags & FST_ATIM ? Number(atim / 1000000n) : current.atimeMs;
|
|
19726
|
+
const mtime = flags & FST_MTIM_NOW ? now : flags & FST_MTIM ? Number(mtim / 1000000n) : current.mtimeMs;
|
|
19727
|
+
this.opts.vfs.utimes(path, atime, mtime, this.opts.cred);
|
|
19728
|
+
return WASI_ESUCCESS;
|
|
19729
|
+
}
|
|
19730
|
+
writeFilestat(out, stat2) {
|
|
19731
|
+
const view = this.view;
|
|
19732
|
+
zero(this.bytes, out, SIZEOF_FILESTAT);
|
|
19733
|
+
view.setBigUint64(out, BigInt(stat2.dev), true);
|
|
19734
|
+
view.setBigUint64(out + 8, BigInt(stat2.ino), true);
|
|
19735
|
+
view.setUint8(out + 16, filetypeOf(stat2.mode));
|
|
19736
|
+
view.setBigUint64(out + 24, BigInt(stat2.nlink), true);
|
|
19737
|
+
view.setBigUint64(out + 32, BigInt(stat2.size), true);
|
|
19738
|
+
view.setBigUint64(out + 40, msToNs(stat2.atimeMs), true);
|
|
19739
|
+
view.setBigUint64(out + 48, msToNs(stat2.mtimeMs), true);
|
|
19740
|
+
view.setBigUint64(out + 56, msToNs(stat2.ctimeMs), true);
|
|
19741
|
+
}
|
|
19742
|
+
// ── preopens ─────────────────────────────────────────────────────────────
|
|
19743
|
+
fd_prestat_get(fd, out) {
|
|
19744
|
+
const descriptor = this.fds.get(fd);
|
|
19745
|
+
if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
|
|
19746
|
+
throw new WasiError(WASI_EBADF);
|
|
19747
|
+
}
|
|
19748
|
+
const view = this.view;
|
|
19749
|
+
view.setUint8(
|
|
19750
|
+
out,
|
|
19751
|
+
0
|
|
19752
|
+
/* preopentype: dir */
|
|
19753
|
+
);
|
|
19754
|
+
view.setUint32(out + 4, encoder6.encode(descriptor.preopen).length, true);
|
|
19755
|
+
return WASI_ESUCCESS;
|
|
19756
|
+
}
|
|
19757
|
+
fd_prestat_dir_name(fd, ptr, len) {
|
|
19758
|
+
const descriptor = this.fds.get(fd);
|
|
19759
|
+
if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
|
|
19760
|
+
throw new WasiError(WASI_EBADF);
|
|
19761
|
+
}
|
|
19762
|
+
const name = encoder6.encode(descriptor.preopen);
|
|
19763
|
+
if (name.length > len) throw new WasiError(WASI_EINVAL);
|
|
19764
|
+
this.bytes.set(name, ptr);
|
|
19765
|
+
return WASI_ESUCCESS;
|
|
19766
|
+
}
|
|
19767
|
+
// ── paths ────────────────────────────────────────────────────────────────
|
|
19768
|
+
path_open(dirfd, _lookupFlags, pathPtr, pathLen, oflags, rightsBase, _rightsInheriting, fdflags, out) {
|
|
19769
|
+
this.syncPaths();
|
|
19770
|
+
const { abs, root } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19771
|
+
const vfs = this.opts.vfs;
|
|
19772
|
+
const cred = this.opts.cred;
|
|
19773
|
+
const exists = vfs.lexists(abs);
|
|
19774
|
+
if (oflags & O_EXCL && oflags & O_CREAT && exists) throw new WasiError(WASI_EEXIST);
|
|
19775
|
+
if (exists && vfs.stat(abs, { cred }).isDirectory()) {
|
|
19776
|
+
if (rightsBase & RIGHT_FD_WRITE) throw new WasiError(WASI_EISDIR);
|
|
19777
|
+
this.view.setUint32(out, this.allocate({ kind: "dir", path: abs, root, rights: RIGHTS_ALL }), true);
|
|
19778
|
+
return WASI_ESUCCESS;
|
|
19779
|
+
}
|
|
19780
|
+
if (oflags & O_DIRECTORY) throw new WasiError(exists ? WASI_ENOTDIR : WASI_ENOENT);
|
|
19781
|
+
if (!exists) {
|
|
19782
|
+
if (!(oflags & O_CREAT)) throw new WasiError(WASI_ENOENT);
|
|
19783
|
+
vfs.writeFile(abs, new Uint8Array(0), { cred, mode: 420 });
|
|
19784
|
+
}
|
|
19785
|
+
const truncate = (oflags & O_TRUNC) !== 0;
|
|
19786
|
+
const data = truncate ? new Uint8Array(0) : vfs.readFile(abs, cred);
|
|
19787
|
+
if (truncate) vfs.writeFile(abs, data, { cred });
|
|
19788
|
+
const size = data.length;
|
|
19789
|
+
const rights = rightsBase === 0n ? RIGHTS_ALL : rightsBase;
|
|
19790
|
+
const append = (fdflags & FD_APPEND) !== 0;
|
|
19791
|
+
this.view.setUint32(
|
|
19792
|
+
out,
|
|
19793
|
+
this.allocate({
|
|
19794
|
+
kind: "file",
|
|
19795
|
+
path: abs,
|
|
19796
|
+
root,
|
|
19797
|
+
data,
|
|
19798
|
+
size,
|
|
19799
|
+
pos: append ? size : 0,
|
|
19800
|
+
dirty: false,
|
|
19801
|
+
append,
|
|
19802
|
+
rights,
|
|
19803
|
+
flags: fdflags
|
|
19804
|
+
}),
|
|
19805
|
+
true
|
|
19806
|
+
);
|
|
19807
|
+
return WASI_ESUCCESS;
|
|
19808
|
+
}
|
|
19809
|
+
fd_readdir(fd, buf, bufLen, cookie, out) {
|
|
19810
|
+
this.syncPaths();
|
|
19811
|
+
const descriptor = this.dir(fd);
|
|
19812
|
+
const vfs = this.opts.vfs;
|
|
19813
|
+
const cred = this.opts.cred;
|
|
19814
|
+
const entries = [
|
|
19815
|
+
{ name: ".", type: FILETYPE_DIRECTORY },
|
|
19816
|
+
{ name: "..", type: FILETYPE_DIRECTORY },
|
|
19817
|
+
...vfs.readdirWithTypes(descriptor.path, cred).map((entry) => ({
|
|
19818
|
+
name: entry.name,
|
|
19819
|
+
type: entry.kind === "directory" ? FILETYPE_DIRECTORY : entry.kind === "symlink" ? FILETYPE_SYMBOLIC_LINK : entry.kind === "file" ? FILETYPE_REGULAR_FILE : FILETYPE_UNKNOWN
|
|
19820
|
+
}))
|
|
19821
|
+
];
|
|
19822
|
+
const view = this.view;
|
|
19823
|
+
const memory = this.bytes;
|
|
19824
|
+
let written = 0;
|
|
19825
|
+
for (let index = Number(cookie); index < entries.length; index++) {
|
|
19826
|
+
const entry = entries[index];
|
|
19827
|
+
const name = encoder6.encode(entry.name);
|
|
19828
|
+
if (written + SIZEOF_DIRENT > bufLen) break;
|
|
19829
|
+
const at = buf + written;
|
|
19830
|
+
view.setBigUint64(at, BigInt(index + 1), true);
|
|
19831
|
+
view.setBigUint64(at + 8, BigInt(inodeOf(descriptor.path, entry.name)), true);
|
|
19832
|
+
view.setUint32(at + 16, name.length, true);
|
|
19833
|
+
view.setUint8(at + 20, entry.type);
|
|
19834
|
+
written += SIZEOF_DIRENT;
|
|
19835
|
+
const room = Math.min(name.length, bufLen - written);
|
|
19836
|
+
memory.set(name.subarray(0, room), buf + written);
|
|
19837
|
+
written += room;
|
|
19838
|
+
if (room < name.length) break;
|
|
19839
|
+
}
|
|
19840
|
+
view.setUint32(out, written, true);
|
|
19841
|
+
return WASI_ESUCCESS;
|
|
19842
|
+
}
|
|
19843
|
+
path_create_directory(dirfd, pathPtr, pathLen) {
|
|
19844
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19845
|
+
this.opts.vfs.mkdir(abs, { cred: this.opts.cred, mode: 493 });
|
|
19846
|
+
return WASI_ESUCCESS;
|
|
19847
|
+
}
|
|
19848
|
+
path_remove_directory(dirfd, pathPtr, pathLen) {
|
|
19849
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19850
|
+
this.opts.vfs.rmdir(abs, this.opts.cred);
|
|
19851
|
+
return WASI_ESUCCESS;
|
|
19852
|
+
}
|
|
19853
|
+
path_unlink_file(dirfd, pathPtr, pathLen) {
|
|
19854
|
+
this.syncPaths();
|
|
19855
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19856
|
+
this.opts.vfs.unlink(abs, this.opts.cred);
|
|
19857
|
+
return WASI_ESUCCESS;
|
|
19858
|
+
}
|
|
19859
|
+
path_rename(oldDirfd, oldPtr, oldLen, newDirfd, newPtr, newLen) {
|
|
19860
|
+
this.syncPaths();
|
|
19861
|
+
const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
|
|
19862
|
+
const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
|
|
19863
|
+
this.opts.vfs.rename(from.abs, to.abs, this.opts.cred);
|
|
19864
|
+
return WASI_ESUCCESS;
|
|
19865
|
+
}
|
|
19866
|
+
path_symlink(targetPtr, targetLen, dirfd, pathPtr, pathLen) {
|
|
19867
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19868
|
+
this.opts.vfs.symlink(this.readString(targetPtr, targetLen), abs, this.opts.cred);
|
|
19869
|
+
return WASI_ESUCCESS;
|
|
19870
|
+
}
|
|
19871
|
+
path_link(oldDirfd, _lookupFlags, oldPtr, oldLen, newDirfd, newPtr, newLen) {
|
|
19872
|
+
this.syncPaths();
|
|
19873
|
+
const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
|
|
19874
|
+
const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
|
|
19875
|
+
this.opts.vfs.link(from.abs, to.abs, this.opts.cred);
|
|
19876
|
+
return WASI_ESUCCESS;
|
|
19877
|
+
}
|
|
19878
|
+
path_readlink(dirfd, pathPtr, pathLen, buf, bufLen, out) {
|
|
19879
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19880
|
+
const target = encoder6.encode(this.opts.vfs.readlink(abs, this.opts.cred));
|
|
19881
|
+
const room = Math.min(target.length, bufLen);
|
|
19882
|
+
this.bytes.set(target.subarray(0, room), buf);
|
|
19883
|
+
this.view.setUint32(out, room, true);
|
|
19884
|
+
return WASI_ESUCCESS;
|
|
19885
|
+
}
|
|
19886
|
+
// ── polling ──────────────────────────────────────────────────────────────
|
|
19887
|
+
/**
|
|
19888
|
+
* `poll_oneoff`, which is how a WASI guest sleeps and how it waits on I/O.
|
|
19889
|
+
*
|
|
19890
|
+
* Files and the standard streams are always ready here — nothing in this
|
|
19891
|
+
* container can leave a read pending, since stdin was drained before the
|
|
19892
|
+
* guest started. That leaves the clock, which is the case that matters:
|
|
19893
|
+
* `sleep()` compiles to a lone clock subscription, and it is honoured by
|
|
19894
|
+
* actually waiting, in slices, so that killing the process interrupts it.
|
|
19895
|
+
*/
|
|
19896
|
+
poll_oneoff(inPtr, outPtr, count, nevents) {
|
|
19897
|
+
if (count === 0) {
|
|
19898
|
+
this.view.setUint32(nevents, 0, true);
|
|
19899
|
+
return WASI_ESUCCESS;
|
|
19900
|
+
}
|
|
19901
|
+
const subscriptions = [];
|
|
19902
|
+
for (let index = 0; index < count; index++) {
|
|
19903
|
+
const at = inPtr + index * SIZEOF_SUBSCRIPTION;
|
|
19904
|
+
const view2 = this.view;
|
|
19905
|
+
subscriptions.push({
|
|
19906
|
+
userdata: view2.getBigUint64(at, true),
|
|
19907
|
+
type: view2.getUint8(at + 8),
|
|
19908
|
+
fd: view2.getUint32(at + 16, true),
|
|
19909
|
+
clockId: view2.getUint32(at + 16, true),
|
|
19910
|
+
timeout: view2.getBigUint64(at + 24, true),
|
|
19911
|
+
flags: view2.getUint16(at + 40, true)
|
|
19912
|
+
});
|
|
19913
|
+
}
|
|
19914
|
+
const ready = subscriptions.filter((s) => s.type !== EVENTTYPE_CLOCK);
|
|
19915
|
+
if (ready.length === 0) {
|
|
19916
|
+
const delays = subscriptions.map((s) => {
|
|
19917
|
+
const nanoseconds = (s.flags & SUBSCRIPTION_CLOCK_ABSTIME) !== 0 ? s.timeout - this.clockNow(s.clockId) : s.timeout;
|
|
19918
|
+
return Number(nanoseconds < 0n ? 0n : nanoseconds) / 1e6;
|
|
19919
|
+
});
|
|
19920
|
+
this.sleepInterruptibly(Math.min(...delays));
|
|
19921
|
+
}
|
|
19922
|
+
const events = ready.length > 0 ? ready : subscriptions;
|
|
19923
|
+
const view = this.view;
|
|
19924
|
+
events.forEach((subscription, index) => {
|
|
19925
|
+
const at = outPtr + index * SIZEOF_EVENT;
|
|
19926
|
+
zero(this.bytes, at, SIZEOF_EVENT);
|
|
19927
|
+
view.setBigUint64(at, subscription.userdata, true);
|
|
19928
|
+
view.setUint16(at + 8, WASI_ESUCCESS, true);
|
|
19929
|
+
view.setUint8(at + 10, subscription.type);
|
|
19930
|
+
if (subscription.type === EVENTTYPE_FD_READ) {
|
|
19931
|
+
const available = subscription.fd === 0 ? this.opts.stdin.available : this.bytesLeft(subscription.fd);
|
|
19932
|
+
view.setBigUint64(at + 16, BigInt(available), true);
|
|
19933
|
+
} else if (subscription.type === EVENTTYPE_FD_WRITE) {
|
|
19934
|
+
view.setBigUint64(at + 16, BigInt(64 * 1024), true);
|
|
19935
|
+
}
|
|
19936
|
+
});
|
|
19937
|
+
view.setUint32(nevents, events.length, true);
|
|
19938
|
+
return WASI_ESUCCESS;
|
|
19939
|
+
}
|
|
19940
|
+
bytesLeft(fd) {
|
|
19941
|
+
const descriptor = this.fds.get(fd);
|
|
19942
|
+
return descriptor?.kind === "file" ? Math.max(0, descriptor.size - descriptor.pos) : 0;
|
|
19943
|
+
}
|
|
19944
|
+
/** Wait in slices so that a `SIGKILL` does not have to outlast the sleep. */
|
|
19945
|
+
sleepInterruptibly(milliseconds) {
|
|
19946
|
+
let remaining = milliseconds;
|
|
19947
|
+
while (remaining > 0) {
|
|
19948
|
+
if (this.opts.aborted()) return;
|
|
19949
|
+
const slice = Math.min(remaining, 50);
|
|
19950
|
+
this.opts.sleep(slice);
|
|
19951
|
+
remaining -= slice;
|
|
19952
|
+
}
|
|
19953
|
+
}
|
|
19954
|
+
};
|
|
19955
|
+
var WasiError = class extends Error {
|
|
19956
|
+
constructor(errno) {
|
|
19957
|
+
super(`wasi errno ${errno}`);
|
|
19958
|
+
this.errno = errno;
|
|
19959
|
+
this.name = "WasiError";
|
|
19960
|
+
}
|
|
19961
|
+
errno;
|
|
19962
|
+
};
|
|
19963
|
+
function errnoOf(error) {
|
|
19964
|
+
if (error instanceof WasiError) return error.errno;
|
|
19965
|
+
if (isSysError(error)) return WASI_ERRNO_BY_CODE[error.code] ?? WASI_EIO;
|
|
19966
|
+
if (error instanceof RangeError) return WASI_EFAULT;
|
|
19967
|
+
return WASI_EIO;
|
|
19968
|
+
}
|
|
19969
|
+
function filetypeOf(mode) {
|
|
19970
|
+
const kind = mode & 61440;
|
|
19971
|
+
if (kind === S_IFDIR) return FILETYPE_DIRECTORY;
|
|
19972
|
+
if (kind === S_IFREG) return FILETYPE_REGULAR_FILE;
|
|
19973
|
+
if (kind === S_IFLNK) return FILETYPE_SYMBOLIC_LINK;
|
|
19974
|
+
if (kind === S_IFCHR) return FILETYPE_CHARACTER_DEVICE;
|
|
19975
|
+
return FILETYPE_UNKNOWN;
|
|
19976
|
+
}
|
|
19977
|
+
function msToNs(milliseconds) {
|
|
19978
|
+
return BigInt(Math.round(milliseconds)) * 1000000n;
|
|
19979
|
+
}
|
|
19980
|
+
function zero(memory, at, length) {
|
|
19981
|
+
memory.fill(0, at, at + length);
|
|
19982
|
+
}
|
|
19983
|
+
function reserve(descriptor, length) {
|
|
19984
|
+
if (length <= descriptor.data.length) return;
|
|
19985
|
+
const grown = new Uint8Array(Math.max(length, descriptor.data.length * 2, 1024));
|
|
19986
|
+
grown.set(descriptor.data.subarray(0, descriptor.size));
|
|
19987
|
+
descriptor.data = grown;
|
|
19988
|
+
}
|
|
19989
|
+
function inodeOf(dir3, name) {
|
|
19990
|
+
let hash = 2166136261;
|
|
19991
|
+
const path = `${dir3}/${name}`;
|
|
19992
|
+
for (let index = 0; index < path.length; index++) {
|
|
19993
|
+
hash ^= path.charCodeAt(index);
|
|
19994
|
+
hash = Math.imul(hash, 16777619);
|
|
19995
|
+
}
|
|
19996
|
+
return hash >>> 0;
|
|
19997
|
+
}
|
|
19998
|
+
function defaultHrtime() {
|
|
19999
|
+
return BigInt(Math.round(performance.now() * 1e6));
|
|
20000
|
+
}
|
|
20001
|
+
function defaultRandom(into) {
|
|
20002
|
+
crypto.getRandomValues(into);
|
|
20003
|
+
}
|
|
20004
|
+
function defaultSleep(milliseconds) {
|
|
20005
|
+
try {
|
|
20006
|
+
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
20007
|
+
Atomics.wait(buffer, 0, 0, milliseconds);
|
|
20008
|
+
} catch {
|
|
20009
|
+
}
|
|
20010
|
+
}
|
|
20011
|
+
|
|
20012
|
+
// src/wasi/run.ts
|
|
20013
|
+
var compiled2 = /* @__PURE__ */ new Map();
|
|
20014
|
+
var MAX_CACHED_MODULES = 32;
|
|
20015
|
+
async function runWasi(ctx, bytes2, options = {}) {
|
|
20016
|
+
if (!isWasmBinary(bytes2)) {
|
|
20017
|
+
ctx.warn("cannot execute binary file: not a WebAssembly module");
|
|
20018
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20019
|
+
}
|
|
20020
|
+
const argv = options.argv ?? ctx.argv;
|
|
20021
|
+
const host2 = new WasiHost({
|
|
20022
|
+
argv,
|
|
20023
|
+
/* `PWD` is set truthfully, but do not mistake it for a working directory:
|
|
20024
|
+
* wasi-libc keeps its own cwd, fixed at `/`, and ignores this. A guest's
|
|
20025
|
+
* relative path therefore resolves from the root — the same thing it does
|
|
20026
|
+
* under wasmtime, and why programs run here are given absolute paths. */
|
|
20027
|
+
env: { ...ctx.env, PWD: ctx.cwd },
|
|
20028
|
+
vfs: ctx.vfs,
|
|
20029
|
+
cred: ctx.cred,
|
|
20030
|
+
cwd: ctx.cwd,
|
|
20031
|
+
stdin: await primeStdin(ctx),
|
|
20032
|
+
stdout: ctx.stdout,
|
|
20033
|
+
stderr: ctx.stderr,
|
|
20034
|
+
...options.preopens ? { preopens: options.preopens } : {},
|
|
20035
|
+
now: ctx.kernel.now,
|
|
20036
|
+
aborted: () => ctx.signal.aborted
|
|
20037
|
+
});
|
|
20038
|
+
let instance;
|
|
20039
|
+
try {
|
|
20040
|
+
const module = await compile(bytes2);
|
|
20041
|
+
instance = await WebAssembly.instantiate(module, {
|
|
20042
|
+
...options.imports,
|
|
20043
|
+
wasi_snapshot_preview1: host2.wasiImport,
|
|
20044
|
+
wasi_unstable: host2.wasiUnstableImport
|
|
20045
|
+
});
|
|
20046
|
+
} catch (error) {
|
|
20047
|
+
ctx.warn(describeStartupFailure(error));
|
|
20048
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20049
|
+
}
|
|
20050
|
+
try {
|
|
20051
|
+
host2.bind(instance);
|
|
20052
|
+
} catch (error) {
|
|
20053
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
20054
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20055
|
+
}
|
|
20056
|
+
const start2 = instance.exports._start ?? instance.exports._initialize;
|
|
20057
|
+
if (typeof start2 !== "function") {
|
|
20058
|
+
ctx.warn("not a WASI command: the module exports neither _start nor _initialize");
|
|
20059
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20060
|
+
}
|
|
20061
|
+
try {
|
|
20062
|
+
start2();
|
|
20063
|
+
return host2.exitCode ?? 0;
|
|
20064
|
+
} catch (error) {
|
|
20065
|
+
if (error instanceof WasiExit) return error.code;
|
|
20066
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
20067
|
+
return 134;
|
|
20068
|
+
} finally {
|
|
20069
|
+
try {
|
|
20070
|
+
host2.flushAll();
|
|
20071
|
+
} catch (error) {
|
|
20072
|
+
ctx.warn(`failed to flush open files: ${error instanceof Error ? error.message : String(error)}`);
|
|
20073
|
+
}
|
|
20074
|
+
}
|
|
20075
|
+
}
|
|
20076
|
+
async function compile(bytes2) {
|
|
20077
|
+
const key = await digest(bytes2);
|
|
20078
|
+
const hit = compiled2.get(key);
|
|
20079
|
+
if (hit) {
|
|
20080
|
+
compiled2.delete(key);
|
|
20081
|
+
compiled2.set(key, hit);
|
|
20082
|
+
return hit;
|
|
20083
|
+
}
|
|
20084
|
+
const module = await WebAssembly.compile(sliceForWasm(bytes2));
|
|
20085
|
+
compiled2.set(key, module);
|
|
20086
|
+
if (compiled2.size > MAX_CACHED_MODULES) {
|
|
20087
|
+
const oldest = compiled2.keys().next().value;
|
|
20088
|
+
if (oldest !== void 0) compiled2.delete(oldest);
|
|
20089
|
+
}
|
|
20090
|
+
return module;
|
|
20091
|
+
}
|
|
20092
|
+
async function digest(bytes2) {
|
|
20093
|
+
const hash = await crypto.subtle.digest("SHA-256", sliceForWasm(bytes2));
|
|
20094
|
+
return Array.from(new Uint8Array(hash, 0, 16), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20095
|
+
}
|
|
20096
|
+
function sliceForWasm(bytes2) {
|
|
20097
|
+
return bytes2.buffer.slice(bytes2.byteOffset, bytes2.byteOffset + bytes2.byteLength);
|
|
20098
|
+
}
|
|
20099
|
+
async function primeStdin(ctx) {
|
|
20100
|
+
let pending = new Uint8Array(0);
|
|
20101
|
+
if (!ctx.stdin.isTTY) {
|
|
20102
|
+
try {
|
|
20103
|
+
pending = await ctx.stdin.readAll();
|
|
20104
|
+
} catch {
|
|
20105
|
+
pending = new Uint8Array(0);
|
|
20106
|
+
}
|
|
20107
|
+
}
|
|
20108
|
+
return {
|
|
20109
|
+
read(size) {
|
|
20110
|
+
const chunk = pending.slice(0, Math.min(size, pending.length));
|
|
20111
|
+
pending = pending.subarray(chunk.length);
|
|
20112
|
+
return chunk;
|
|
20113
|
+
},
|
|
20114
|
+
get available() {
|
|
20115
|
+
return pending.length;
|
|
20116
|
+
},
|
|
20117
|
+
isTTY: ctx.stdin.isTTY
|
|
20118
|
+
};
|
|
20119
|
+
}
|
|
20120
|
+
function describeStartupFailure(error) {
|
|
20121
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20122
|
+
if (error instanceof WebAssembly.LinkError) {
|
|
20123
|
+
return `${message} \u2014 the module needs imports this container does not provide. Build it for wasm32-wasi (a plain wasm32-unknown-unknown or emscripten build will not run here).`;
|
|
20124
|
+
}
|
|
20125
|
+
return message;
|
|
20126
|
+
}
|
|
20127
|
+
|
|
20128
|
+
// src/wasi/command.ts
|
|
20129
|
+
var wasi = defineCommand({
|
|
20130
|
+
name: "wasi",
|
|
20131
|
+
path: "/usr/bin/wasi",
|
|
20132
|
+
summary: "run a WebAssembly (wasm32-wasi) binary",
|
|
20133
|
+
usage: "wasi [--dir PATH] [--mapdir GUEST=PATH] [--env K=V] MODULE.wasm [args...]",
|
|
20134
|
+
manual: `Runs a WebAssembly module built for wasm32-wasi as a container
|
|
20135
|
+
process: it sees the container's filesystem, environment and standard streams,
|
|
20136
|
+
and can be piped and redirected like any other command.
|
|
20137
|
+
|
|
20138
|
+
By default the module is given the whole filesystem, which is what an ordinary
|
|
20139
|
+
program compiled by clang, Rust, Zig or TinyGo expects. Passing --dir or
|
|
20140
|
+
--mapdir replaces that default with exactly what is listed, confining the
|
|
20141
|
+
module to those directories.
|
|
20142
|
+
|
|
20143
|
+
A WASI program has no working directory: wasi-libc fixes its own at / and
|
|
20144
|
+
resolves relative paths from there, exactly as it does under wasmtime. Pass
|
|
20145
|
+
absolute paths, or preopen a directory with --mapdir to give the program a
|
|
20146
|
+
name for it.
|
|
20147
|
+
|
|
20148
|
+
Executable .wasm files are dispatched here automatically, so ./tool.wasm runs
|
|
20149
|
+
without naming this command.
|
|
20150
|
+
|
|
20151
|
+
Sockets are not available: this container's network is an HTTP router rather
|
|
20152
|
+
than a packet path, and socket calls report ENOTSUP.`,
|
|
20153
|
+
async run(ctx) {
|
|
20154
|
+
const preopens = {};
|
|
20155
|
+
const env2 = {};
|
|
20156
|
+
let module;
|
|
20157
|
+
let rest = [];
|
|
20158
|
+
const args = ctx.args;
|
|
20159
|
+
for (let index = 0; index < args.length; index++) {
|
|
20160
|
+
const arg = args[index];
|
|
20161
|
+
if (arg === "--help" || arg === "-h") {
|
|
20162
|
+
ctx.line(wasi.usage);
|
|
20163
|
+
return 0;
|
|
20164
|
+
}
|
|
20165
|
+
if (arg === "--") {
|
|
20166
|
+
module = args[index + 1];
|
|
20167
|
+
rest = args.slice(index + 2);
|
|
20168
|
+
break;
|
|
20169
|
+
}
|
|
20170
|
+
if (arg === "--dir") {
|
|
20171
|
+
const dir3 = args[++index];
|
|
20172
|
+
if (dir3 === void 0) return ctx.reportError(new UsageError("--dir needs a path"));
|
|
20173
|
+
preopens[ctx.path(dir3)] = ctx.path(dir3);
|
|
20174
|
+
continue;
|
|
20175
|
+
}
|
|
20176
|
+
if (arg === "--mapdir") {
|
|
20177
|
+
const pair = args[++index];
|
|
20178
|
+
const split2 = pair?.indexOf("=") ?? -1;
|
|
20179
|
+
if (pair === void 0 || split2 < 0) {
|
|
20180
|
+
return ctx.reportError(new UsageError("--mapdir needs GUEST=PATH"));
|
|
20181
|
+
}
|
|
20182
|
+
preopens[pair.slice(0, split2)] = ctx.path(pair.slice(split2 + 1));
|
|
20183
|
+
continue;
|
|
20184
|
+
}
|
|
20185
|
+
if (arg === "--env") {
|
|
20186
|
+
const pair = args[++index];
|
|
20187
|
+
const split2 = pair?.indexOf("=") ?? -1;
|
|
20188
|
+
if (pair === void 0 || split2 < 0) {
|
|
20189
|
+
return ctx.reportError(new UsageError("--env needs KEY=VALUE"));
|
|
20190
|
+
}
|
|
20191
|
+
env2[pair.slice(0, split2)] = pair.slice(split2 + 1);
|
|
20192
|
+
continue;
|
|
20193
|
+
}
|
|
20194
|
+
module = arg;
|
|
20195
|
+
rest = args.slice(index + 1);
|
|
20196
|
+
break;
|
|
20197
|
+
}
|
|
20198
|
+
if (module === void 0) return ctx.reportError(new UsageError("a module is required"));
|
|
20199
|
+
const abs = ctx.path(module);
|
|
20200
|
+
let bytes2;
|
|
20201
|
+
try {
|
|
20202
|
+
bytes2 = ctx.vfs.readFile(abs, ctx.cred);
|
|
20203
|
+
} catch (error) {
|
|
20204
|
+
ctx.reportError(error, abs);
|
|
20205
|
+
return EXIT_NOT_FOUND;
|
|
20206
|
+
}
|
|
20207
|
+
if (!isWasmBinary(bytes2)) {
|
|
20208
|
+
return ctx.fail(`${abs}: not a WebAssembly module`, 126);
|
|
20209
|
+
}
|
|
20210
|
+
Object.assign(ctx.env, env2);
|
|
20211
|
+
return await runWasi(ctx, bytes2, {
|
|
20212
|
+
/* argv[0] is the path as the user wrote it, not the resolved one: a
|
|
20213
|
+
* program printing its own usage should echo what was typed. */
|
|
20214
|
+
argv: [module, ...rest],
|
|
20215
|
+
...Object.keys(preopens).length > 0 ? { preopens } : {}
|
|
20216
|
+
});
|
|
20217
|
+
}
|
|
20218
|
+
});
|
|
20219
|
+
function wasiCommands() {
|
|
20220
|
+
return [wasi];
|
|
20221
|
+
}
|
|
18998
20222
|
var platformBuffer = globalThis.Buffer;
|
|
18999
20223
|
var Buffer2 = platformBuffer ?? addBase64UrlSupport(Buffer$1);
|
|
19000
20224
|
function addBase64UrlSupport(BufferClass) {
|
|
@@ -19185,8 +20409,8 @@ function verifyIntegrity(data, integrity, shasum, identity) {
|
|
|
19185
20409
|
const dash = choice.indexOf("-");
|
|
19186
20410
|
const algorithm = choice.slice(0, dash);
|
|
19187
20411
|
const expected = choice.slice(dash + 1).replace(/\?.*$/, "");
|
|
19188
|
-
const
|
|
19189
|
-
if (
|
|
20412
|
+
const digest2 = algorithm === "sha512" ? sha512(data) : algorithm === "sha256" ? sha256(data) : null;
|
|
20413
|
+
if (digest2 && Buffer2.from(digest2).toString("base64") === expected) return;
|
|
19190
20414
|
}
|
|
19191
20415
|
throw new Error(`Integrity check failed for ${identity}`);
|
|
19192
20416
|
}
|
|
@@ -20156,6 +21380,7 @@ function allCommands() {
|
|
|
20156
21380
|
...nodeCommands(),
|
|
20157
21381
|
...pythonCommands(),
|
|
20158
21382
|
...ffmpegCommands(),
|
|
21383
|
+
...wasiCommands(),
|
|
20159
21384
|
...packageCommands()
|
|
20160
21385
|
];
|
|
20161
21386
|
}
|
|
@@ -20315,15 +21540,15 @@ var Session = class {
|
|
|
20315
21540
|
async run(command, opts = {}) {
|
|
20316
21541
|
if (this.closed) throw new Error("session is closed");
|
|
20317
21542
|
const combined = [];
|
|
20318
|
-
const
|
|
21543
|
+
const decoder9 = new TextDecoder();
|
|
20319
21544
|
const stdout = new BufferSink((chunk) => {
|
|
20320
|
-
const text2 =
|
|
21545
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
20321
21546
|
combined.push(text2);
|
|
20322
21547
|
opts.onStdout?.(text2);
|
|
20323
21548
|
this.hooks.onStdout?.(text2);
|
|
20324
21549
|
});
|
|
20325
21550
|
const stderr = new BufferSink((chunk) => {
|
|
20326
|
-
const text2 =
|
|
21551
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
20327
21552
|
combined.push(text2);
|
|
20328
21553
|
opts.onStderr?.(text2);
|
|
20329
21554
|
this.hooks.onStderr?.(text2);
|
|
@@ -22777,8 +24002,8 @@ function createCryptoModule() {
|
|
|
22777
24002
|
queueMicrotask(() => done?.(null, target));
|
|
22778
24003
|
},
|
|
22779
24004
|
hash(algorithm, data, encoding = "hex") {
|
|
22780
|
-
const
|
|
22781
|
-
return encoding === "buffer" ?
|
|
24005
|
+
const digest2 = Buffer2.from(hashFor(algorithm)(toBytes2(data)));
|
|
24006
|
+
return encoding === "buffer" ? digest2 : digest2.toString(encoding);
|
|
22782
24007
|
},
|
|
22783
24008
|
timingSafeEqual(a, b) {
|
|
22784
24009
|
if (a.length !== b.length) throw new RangeError("Input buffers must have the same byte length");
|
|
@@ -24713,7 +25938,7 @@ var MemoryStat = class {
|
|
|
24713
25938
|
return this.inode.kind === "symlink";
|
|
24714
25939
|
}
|
|
24715
25940
|
};
|
|
24716
|
-
var
|
|
25941
|
+
var encoder7 = new TextEncoder();
|
|
24717
25942
|
var MemoryVolume = class {
|
|
24718
25943
|
entries = /* @__PURE__ */ new Map();
|
|
24719
25944
|
nextIno = 2;
|
|
@@ -24731,7 +25956,7 @@ var MemoryVolume = class {
|
|
|
24731
25956
|
writeFileSync(path, data) {
|
|
24732
25957
|
const key = this.key(path);
|
|
24733
25958
|
this.requireParent(key, "open");
|
|
24734
|
-
const bytes2 = typeof data === "string" ?
|
|
25959
|
+
const bytes2 = typeof data === "string" ? encoder7.encode(data) : data.slice();
|
|
24735
25960
|
const current = this.entries.get(key);
|
|
24736
25961
|
if (current?.kind === "directory") throw new VolumeError("EISDIR", "open", key);
|
|
24737
25962
|
if (current?.kind === "symlink") throw new VolumeError("EINVAL", "open", key);
|
|
@@ -24744,7 +25969,7 @@ var MemoryVolume = class {
|
|
|
24744
25969
|
}
|
|
24745
25970
|
appendFileSync(path, data) {
|
|
24746
25971
|
const key = this.key(path);
|
|
24747
|
-
const bytes2 = typeof data === "string" ?
|
|
25972
|
+
const bytes2 = typeof data === "string" ? encoder7.encode(data) : data;
|
|
24748
25973
|
const current = this.entries.get(key);
|
|
24749
25974
|
if (!current) return this.writeFileSync(key, bytes2);
|
|
24750
25975
|
if (current.kind !== "file") throw new VolumeError(current.kind === "directory" ? "EISDIR" : "EINVAL", "open", key);
|
|
@@ -24882,7 +26107,7 @@ var MemoryVolume = class {
|
|
|
24882
26107
|
if (node2.kind === "directory") directoryCount++;
|
|
24883
26108
|
else {
|
|
24884
26109
|
fileCount++;
|
|
24885
|
-
totalBytes += node2.kind === "file" ? node2.data.length :
|
|
26110
|
+
totalBytes += node2.kind === "file" ? node2.data.length : encoder7.encode(node2.target).length;
|
|
24886
26111
|
}
|
|
24887
26112
|
}
|
|
24888
26113
|
return { totalBytes, fileCount, directoryCount, dirCount: directoryCount };
|
|
@@ -25764,10 +26989,10 @@ function attachRolldownMirror(binding, volume, root) {
|
|
|
25764
26989
|
}
|
|
25765
26990
|
|
|
25766
26991
|
// src/runtime/remote-volume.ts
|
|
25767
|
-
var
|
|
25768
|
-
var
|
|
26992
|
+
var encoder8 = new TextEncoder();
|
|
26993
|
+
var decoder8 = new TextDecoder();
|
|
25769
26994
|
function encodeFrame(header, body) {
|
|
25770
|
-
const json =
|
|
26995
|
+
const json = encoder8.encode(JSON.stringify(header));
|
|
25771
26996
|
const frame = new Uint8Array(4 + json.length + (body?.length ?? 0));
|
|
25772
26997
|
new DataView(frame.buffer).setUint32(0, json.length, true);
|
|
25773
26998
|
frame.set(json, 4);
|
|
@@ -25777,7 +27002,7 @@ function encodeFrame(header, body) {
|
|
|
25777
27002
|
function decodeFrame(frame) {
|
|
25778
27003
|
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
25779
27004
|
const headerLength = view.getUint32(0, true);
|
|
25780
|
-
const header = JSON.parse(
|
|
27005
|
+
const header = JSON.parse(decoder8.decode(frame.subarray(4, 4 + headerLength)));
|
|
25781
27006
|
return { header, body: frame.subarray(4 + headerLength) };
|
|
25782
27007
|
}
|
|
25783
27008
|
function flattenStat(stat2) {
|
|
@@ -26580,15 +27805,15 @@ var Container = class _Container {
|
|
|
26580
27805
|
}
|
|
26581
27806
|
makeStdio(opts) {
|
|
26582
27807
|
const combined = [];
|
|
26583
|
-
const
|
|
27808
|
+
const decoder9 = new TextDecoder();
|
|
26584
27809
|
const stdout = new BufferSink((chunk) => {
|
|
26585
|
-
const text2 =
|
|
27810
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
26586
27811
|
combined.push(text2);
|
|
26587
27812
|
opts.onStdout?.(text2);
|
|
26588
27813
|
this.hooks.onStdout?.(text2);
|
|
26589
27814
|
});
|
|
26590
27815
|
const stderr = new BufferSink((chunk) => {
|
|
26591
|
-
const text2 =
|
|
27816
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
26592
27817
|
combined.push(text2);
|
|
26593
27818
|
opts.onStderr?.(text2);
|
|
26594
27819
|
this.hooks.onStderr?.(text2);
|
|
@@ -27292,6 +28517,6 @@ async function renderInto(box, element, options = { port: 80 }) {
|
|
|
27292
28517
|
// src/index.ts
|
|
27293
28518
|
var src_default = createContainer;
|
|
27294
28519
|
|
|
27295
|
-
export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo };
|
|
28520
|
+
export { ArithError, BufferSink, CallbackSink, CleanPackageInstaller, CommandRegistry, CommonJsEngine, Container, ContainerFs, ERRNO, FileInput, FileOutput, IncompleteInputError, KERNEL_NAME, KERNEL_RELEASE, Kernel, LocalRuntimePod, MemoryVolume, NODE_VERSION, NPM_VERSION, NetworkStack, NullInput, NullOutput, OS_RELEASE, PYTHON_VERSION, Pipe, Process, ProcessTable, ROOT_CRED, SIGNALS, SIGNAL_NAMES, Session, Shell, ShellExit, Lexer as ShellLexer, ShellSyntaxError, Stats, SysError, TeeOutput, Terminal, UserDatabase, Variables, Vfs, VirtualHttpRouter, VirtualHttpServer, VirtualIncomingMessage, VirtualServerResponse, WASM_ALIASES, WasiError, WasiExit, WasiHost, WorkerRuntimePod, allCommands, applyChmod, braceExpand, buildRootfs, builtinNames, captureStdio, configureCPython, configurePython, createChildProcessModule, createContainer, createContext, createCoreModules, createPreview, src_default as default, defineCommand, evalArith, exitCodeForSignal, expandPrompt, expandWord, expandWords, extractNpmTarball, fnmatch, formatMode, getBuiltin, glob, globToRegex, hasMagic, installUserland, isBuiltinName, isCPythonAvailable, isPythonAvailable, isSysError, isWasmBinary, looksLikeEsm, makeCred, normalizeSignal, octalMode, parse as parseShell, parseUmask, path_exports as posixPath, renderInto, resetPidCounter, runWasi, shellQuote, startRuntimeWorker, strerror, syncChannelSupported, transformEsm, unameInfo, wasi as wasiCommand };
|
|
27296
28521
|
//# sourceMappingURL=index.js.map
|
|
27297
28522
|
//# sourceMappingURL=index.js.map
|