sandboxedjs 0.1.34 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -0
- package/bin/sandboxedjs.mjs +5 -3
- package/dist/index.cjs +1379 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +261 -1
- package/dist/index.d.ts +261 -1
- package/dist/index.js +1374 -39
- package/dist/index.js.map +1 -1
- package/dist/rolldown-wasi-worker.js +71 -0
- package/dist/rolldown-wasi-worker.js.map +1 -0
- package/dist/worker-entry.js +33 -4
- package/dist/worker-entry.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -21,6 +21,10 @@ var timersModule = require('timers-browserify');
|
|
|
21
21
|
var legacyUrl = require('url/url.js');
|
|
22
22
|
var legacy$1 = require('@noble/hashes/legacy');
|
|
23
23
|
var hmac = require('@noble/hashes/hmac');
|
|
24
|
+
var module$1 = require('module');
|
|
25
|
+
var fs = require('fs');
|
|
26
|
+
var path = require('path');
|
|
27
|
+
var url = require('url');
|
|
24
28
|
|
|
25
29
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
26
30
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -791,10 +795,10 @@ function globToRegexSource(pattern, opts = {}) {
|
|
|
791
795
|
continue;
|
|
792
796
|
}
|
|
793
797
|
if (c === "[") {
|
|
794
|
-
const
|
|
795
|
-
if (
|
|
796
|
-
out += guardDot() +
|
|
797
|
-
i =
|
|
798
|
+
const compiled3 = compileBracket(pattern, i);
|
|
799
|
+
if (compiled3) {
|
|
800
|
+
out += guardDot() + compiled3.source;
|
|
801
|
+
i = compiled3.next;
|
|
798
802
|
atSegmentStart = false;
|
|
799
803
|
continue;
|
|
800
804
|
}
|
|
@@ -5707,8 +5711,101 @@ function strerrorOf(e) {
|
|
|
5707
5711
|
var EXIT_NOT_EXECUTABLE = 126;
|
|
5708
5712
|
var EXIT_NOT_FOUND = 127;
|
|
5709
5713
|
|
|
5714
|
+
// src/wasi/constants.ts
|
|
5715
|
+
var WASI_ESUCCESS = 0;
|
|
5716
|
+
var WASI_EACCES = 2;
|
|
5717
|
+
var WASI_EAGAIN = 6;
|
|
5718
|
+
var WASI_EBADF = 8;
|
|
5719
|
+
var WASI_EBUSY = 10;
|
|
5720
|
+
var WASI_EEXIST = 20;
|
|
5721
|
+
var WASI_EFAULT = 21;
|
|
5722
|
+
var WASI_EFBIG = 22;
|
|
5723
|
+
var WASI_EINVAL = 28;
|
|
5724
|
+
var WASI_EIO = 29;
|
|
5725
|
+
var WASI_EISDIR = 31;
|
|
5726
|
+
var WASI_ELOOP = 32;
|
|
5727
|
+
var WASI_EMFILE = 34;
|
|
5728
|
+
var WASI_ENAMETOOLONG = 37;
|
|
5729
|
+
var WASI_ENOENT = 44;
|
|
5730
|
+
var WASI_ENOMEM = 48;
|
|
5731
|
+
var WASI_ENOSPC = 51;
|
|
5732
|
+
var WASI_ENOSYS = 52;
|
|
5733
|
+
var WASI_ENOTDIR = 54;
|
|
5734
|
+
var WASI_ENOTEMPTY = 55;
|
|
5735
|
+
var WASI_ENOTSUP = 58;
|
|
5736
|
+
var WASI_ENOTTY = 59;
|
|
5737
|
+
var WASI_EPERM = 63;
|
|
5738
|
+
var WASI_EPIPE = 64;
|
|
5739
|
+
var WASI_EROFS = 69;
|
|
5740
|
+
var WASI_ESPIPE = 70;
|
|
5741
|
+
var WASI_EXDEV = 75;
|
|
5742
|
+
var WASI_ENOTCAPABLE = 76;
|
|
5743
|
+
var WASI_ERRNO_BY_CODE = {
|
|
5744
|
+
EPERM: WASI_EPERM,
|
|
5745
|
+
ENOENT: WASI_ENOENT,
|
|
5746
|
+
EIO: WASI_EIO,
|
|
5747
|
+
EBADF: WASI_EBADF,
|
|
5748
|
+
EAGAIN: WASI_EAGAIN,
|
|
5749
|
+
ENOMEM: WASI_ENOMEM,
|
|
5750
|
+
EACCES: WASI_EACCES,
|
|
5751
|
+
EFAULT: WASI_EFAULT,
|
|
5752
|
+
EBUSY: WASI_EBUSY,
|
|
5753
|
+
EEXIST: WASI_EEXIST,
|
|
5754
|
+
EXDEV: WASI_EXDEV,
|
|
5755
|
+
ENOTDIR: WASI_ENOTDIR,
|
|
5756
|
+
EISDIR: WASI_EISDIR,
|
|
5757
|
+
EINVAL: WASI_EINVAL,
|
|
5758
|
+
EMFILE: WASI_EMFILE,
|
|
5759
|
+
ENOTTY: WASI_ENOTTY,
|
|
5760
|
+
EFBIG: WASI_EFBIG,
|
|
5761
|
+
ENOSPC: WASI_ENOSPC,
|
|
5762
|
+
ESPIPE: WASI_ESPIPE,
|
|
5763
|
+
EROFS: WASI_EROFS,
|
|
5764
|
+
EPIPE: WASI_EPIPE,
|
|
5765
|
+
ENAMETOOLONG: WASI_ENAMETOOLONG,
|
|
5766
|
+
ENOSYS: WASI_ENOSYS,
|
|
5767
|
+
ENOTEMPTY: WASI_ENOTEMPTY,
|
|
5768
|
+
ELOOP: WASI_ELOOP,
|
|
5769
|
+
ENOTSUP: WASI_ENOTSUP
|
|
5770
|
+
};
|
|
5771
|
+
var FILETYPE_UNKNOWN = 0;
|
|
5772
|
+
var FILETYPE_CHARACTER_DEVICE = 2;
|
|
5773
|
+
var FILETYPE_DIRECTORY = 3;
|
|
5774
|
+
var FILETYPE_REGULAR_FILE = 4;
|
|
5775
|
+
var FILETYPE_SYMBOLIC_LINK = 7;
|
|
5776
|
+
var O_CREAT = 1 << 0;
|
|
5777
|
+
var O_DIRECTORY = 1 << 1;
|
|
5778
|
+
var O_EXCL = 1 << 2;
|
|
5779
|
+
var O_TRUNC = 1 << 3;
|
|
5780
|
+
var FD_APPEND = 1 << 0;
|
|
5781
|
+
var LOOKUP_SYMLINK_FOLLOW = 1 << 0;
|
|
5782
|
+
var FST_ATIM = 1 << 0;
|
|
5783
|
+
var FST_ATIM_NOW = 1 << 1;
|
|
5784
|
+
var FST_MTIM = 1 << 2;
|
|
5785
|
+
var FST_MTIM_NOW = 1 << 3;
|
|
5786
|
+
var WHENCE_SET = 0;
|
|
5787
|
+
var WHENCE_CUR = 1;
|
|
5788
|
+
var WHENCE_END = 2;
|
|
5789
|
+
var EVENTTYPE_CLOCK = 0;
|
|
5790
|
+
var EVENTTYPE_FD_READ = 1;
|
|
5791
|
+
var EVENTTYPE_FD_WRITE = 2;
|
|
5792
|
+
var SUBSCRIPTION_CLOCK_ABSTIME = 1 << 0;
|
|
5793
|
+
var RIGHT_FD_READ = 1n << 1n;
|
|
5794
|
+
var RIGHT_FD_SEEK = 1n << 2n;
|
|
5795
|
+
var RIGHT_FD_WRITE = 1n << 6n;
|
|
5796
|
+
var RIGHTS_ALL = (1n << 30n) - 1n;
|
|
5797
|
+
var SIZEOF_FILESTAT = 64;
|
|
5798
|
+
var SIZEOF_FDSTAT = 24;
|
|
5799
|
+
var SIZEOF_DIRENT = 24;
|
|
5800
|
+
var SIZEOF_SUBSCRIPTION = 48;
|
|
5801
|
+
var SIZEOF_EVENT = 32;
|
|
5802
|
+
function isWasmBinary(bytes2) {
|
|
5803
|
+
return bytes2.length >= 4 && bytes2[0] === 0 && bytes2[1] === 97 && bytes2[2] === 115 && bytes2[3] === 109;
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5710
5806
|
// src/kernel/kernel.ts
|
|
5711
5807
|
var BUILTIN_INTERPRETER = "/proc/self/exe";
|
|
5808
|
+
var WASI_INTERPRETER = "/usr/bin/wasi";
|
|
5712
5809
|
var Kernel = class {
|
|
5713
5810
|
vfs;
|
|
5714
5811
|
procs = new ProcessTable();
|
|
@@ -5843,12 +5940,17 @@ var Kernel = class {
|
|
|
5843
5940
|
if (command) return { kind: "builtin", path: candidate, command };
|
|
5844
5941
|
}
|
|
5845
5942
|
let head2 = "";
|
|
5943
|
+
let magic;
|
|
5846
5944
|
try {
|
|
5847
5945
|
if (this.vfs.permitted(st, R_OK, cred)) {
|
|
5848
|
-
|
|
5946
|
+
magic = this.vfs.readFile(real, cred).subarray(0, 256);
|
|
5947
|
+
head2 = new TextDecoder().decode(magic);
|
|
5849
5948
|
}
|
|
5850
5949
|
} catch {
|
|
5851
5950
|
}
|
|
5951
|
+
if (magic && isWasmBinary(magic)) {
|
|
5952
|
+
return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
|
|
5953
|
+
}
|
|
5852
5954
|
if (head2.startsWith("#!")) {
|
|
5853
5955
|
const lineEnd = head2.indexOf("\n");
|
|
5854
5956
|
const line = (lineEnd >= 0 ? head2.slice(2, lineEnd) : head2.slice(2)).trim();
|
|
@@ -5862,6 +5964,7 @@ var Kernel = class {
|
|
|
5862
5964
|
const ext = extname(real);
|
|
5863
5965
|
if (ext === ".js" || ext === ".mjs" || ext === ".cjs") return { kind: "script", path: real, interpreter: ["/usr/bin/node"] };
|
|
5864
5966
|
if (ext === ".py") return { kind: "script", path: real, interpreter: ["/usr/bin/python3"] };
|
|
5967
|
+
if (ext === ".wasm") return { kind: "script", path: real, interpreter: [WASI_INTERPRETER] };
|
|
5865
5968
|
return { kind: "script", path: real, interpreter: ["/bin/sh"] };
|
|
5866
5969
|
}
|
|
5867
5970
|
return null;
|
|
@@ -15740,9 +15843,9 @@ function makeSum(name, algorithm, path) {
|
|
|
15740
15843
|
if (args.has("check")) return checkSums(ctx, algorithm, args.positional, args.has("quiet") || args.has("status"));
|
|
15741
15844
|
const { sources, ok: ok2 } = await readInputs(ctx, args.positional, { stdinName: "-" });
|
|
15742
15845
|
for (const source of sources) {
|
|
15743
|
-
const
|
|
15744
|
-
if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${
|
|
15745
|
-
else ctx.line(`${
|
|
15846
|
+
const digest2 = await digestHex(algorithm, source.bytes);
|
|
15847
|
+
if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${digest2}`);
|
|
15848
|
+
else ctx.line(`${digest2} ${source.name}`);
|
|
15746
15849
|
}
|
|
15747
15850
|
return ok2 ? 0 : 1;
|
|
15748
15851
|
}
|
|
@@ -18523,12 +18626,12 @@ function installSyscallBridge(py) {
|
|
|
18523
18626
|
function bindProgram(py, binding) {
|
|
18524
18627
|
py.__sbxSlot.current = binding;
|
|
18525
18628
|
const { ctx, stdin } = binding;
|
|
18526
|
-
const
|
|
18629
|
+
const decoder9 = new TextDecoder();
|
|
18527
18630
|
py.setStdout({
|
|
18528
|
-
write: (buffer) => (ctx.write(
|
|
18631
|
+
write: (buffer) => (ctx.write(decoder9.decode(buffer)), buffer.length)
|
|
18529
18632
|
});
|
|
18530
18633
|
py.setStderr({
|
|
18531
|
-
write: (buffer) => (ctx.stderr.write(
|
|
18634
|
+
write: (buffer) => (ctx.stderr.write(decoder9.decode(buffer)), buffer.length)
|
|
18532
18635
|
});
|
|
18533
18636
|
py.setStdin({
|
|
18534
18637
|
read: (buffer) => {
|
|
@@ -18578,9 +18681,9 @@ async function resolveIndexUrl() {
|
|
|
18578
18681
|
if (indexUrl) return indexUrl;
|
|
18579
18682
|
if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
|
|
18580
18683
|
try {
|
|
18581
|
-
const { createRequire } = await nodeBuiltin("module");
|
|
18684
|
+
const { createRequire: createRequire2 } = await nodeBuiltin("module");
|
|
18582
18685
|
const path = await nodeBuiltin("path");
|
|
18583
|
-
const require2 =
|
|
18686
|
+
const require2 = createRequire2(path.join(process.cwd(), "index.js"));
|
|
18584
18687
|
return `${path.dirname(require2.resolve("pyodide/package.json"))}/`;
|
|
18585
18688
|
} catch {
|
|
18586
18689
|
return void 0;
|
|
@@ -19045,15 +19148,15 @@ function pythonCommands() {
|
|
|
19045
19148
|
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";
|
|
19046
19149
|
var compiled = null;
|
|
19047
19150
|
async function loadFactory() {
|
|
19048
|
-
const { createRequire } = await nodeBuiltin("module");
|
|
19151
|
+
const { createRequire: createRequire2 } = await nodeBuiltin("module");
|
|
19049
19152
|
const fs = await nodeBuiltin("fs/promises");
|
|
19050
19153
|
const path = await nodeBuiltin("path");
|
|
19051
|
-
const packageRequire =
|
|
19154
|
+
const packageRequire = createRequire2((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
19052
19155
|
let require2 = packageRequire;
|
|
19053
19156
|
try {
|
|
19054
19157
|
require2.resolve("@ffmpeg/core/wasm");
|
|
19055
19158
|
} catch {
|
|
19056
|
-
require2 =
|
|
19159
|
+
require2 = createRequire2(path.join(process.cwd(), "index.js"));
|
|
19057
19160
|
}
|
|
19058
19161
|
const wasmPath = require2.resolve("@ffmpeg/core/wasm");
|
|
19059
19162
|
const wasm = await fs.readFile(wasmPath);
|
|
@@ -19142,6 +19245,1001 @@ var ffprobe = defineCommand({
|
|
|
19142
19245
|
function ffmpegCommands() {
|
|
19143
19246
|
return [ffmpeg, ffprobe];
|
|
19144
19247
|
}
|
|
19248
|
+
|
|
19249
|
+
// src/wasi/host.ts
|
|
19250
|
+
init_path();
|
|
19251
|
+
init_errno();
|
|
19252
|
+
init_mode();
|
|
19253
|
+
var WasiExit = class extends Error {
|
|
19254
|
+
constructor(code) {
|
|
19255
|
+
super(`wasi exit ${code}`);
|
|
19256
|
+
this.code = code;
|
|
19257
|
+
this.name = "WasiExit";
|
|
19258
|
+
}
|
|
19259
|
+
code;
|
|
19260
|
+
};
|
|
19261
|
+
var encoder6 = new TextEncoder();
|
|
19262
|
+
var decoder7 = new TextDecoder();
|
|
19263
|
+
var FIRST_PREOPEN = 3;
|
|
19264
|
+
var WasiHost = class {
|
|
19265
|
+
memory;
|
|
19266
|
+
fds = /* @__PURE__ */ new Map();
|
|
19267
|
+
nextFd = FIRST_PREOPEN;
|
|
19268
|
+
opts;
|
|
19269
|
+
startTime;
|
|
19270
|
+
/** Set once `proc_exit` has run, so the runner reports the guest's code. */
|
|
19271
|
+
exitCode = null;
|
|
19272
|
+
constructor(options) {
|
|
19273
|
+
this.opts = {
|
|
19274
|
+
now: Date.now,
|
|
19275
|
+
hrtime: defaultHrtime,
|
|
19276
|
+
random: defaultRandom,
|
|
19277
|
+
sleep: defaultSleep,
|
|
19278
|
+
aborted: () => false,
|
|
19279
|
+
...options
|
|
19280
|
+
};
|
|
19281
|
+
this.startTime = this.opts.hrtime();
|
|
19282
|
+
this.fds.set(0, { kind: "stdin" });
|
|
19283
|
+
this.fds.set(1, { kind: "stdout", stream: options.stdout });
|
|
19284
|
+
this.fds.set(2, { kind: "stderr", stream: options.stderr });
|
|
19285
|
+
const preopens = options.preopens ?? { "/": "/" };
|
|
19286
|
+
for (const [name, path] of Object.entries(preopens)) {
|
|
19287
|
+
const abs = resolve("/", path);
|
|
19288
|
+
this.fds.set(this.nextFd++, {
|
|
19289
|
+
kind: "dir",
|
|
19290
|
+
path: abs,
|
|
19291
|
+
root: abs,
|
|
19292
|
+
preopen: name,
|
|
19293
|
+
rights: RIGHTS_ALL
|
|
19294
|
+
});
|
|
19295
|
+
}
|
|
19296
|
+
}
|
|
19297
|
+
/** Attach the instance's memory. Called before `_start`. */
|
|
19298
|
+
bind(instance) {
|
|
19299
|
+
const exported = instance.exports.memory;
|
|
19300
|
+
if (!(exported instanceof WebAssembly.Memory)) {
|
|
19301
|
+
throw new Error("wasi: the module does not export a linear memory");
|
|
19302
|
+
}
|
|
19303
|
+
this.memory = exported;
|
|
19304
|
+
}
|
|
19305
|
+
/** Flush every buffered write. Called by the runner when the guest ends. */
|
|
19306
|
+
flushAll() {
|
|
19307
|
+
for (const fd of this.fds.values()) if (fd.kind === "file") this.writeBack(fd);
|
|
19308
|
+
}
|
|
19309
|
+
/**
|
|
19310
|
+
* `wasi_snapshot_preview1`.
|
|
19311
|
+
*
|
|
19312
|
+
* Every entry returns an errno rather than throwing: a JavaScript exception
|
|
19313
|
+
* crossing back into WebAssembly traps the instance, which turns a missing
|
|
19314
|
+
* file into an unrecoverable crash instead of the `ENOENT` the guest is
|
|
19315
|
+
* written to handle. `guard` is what enforces that.
|
|
19316
|
+
*/
|
|
19317
|
+
get wasiImport() {
|
|
19318
|
+
const g = (fn) => ((...args) => this.guard(() => fn.apply(this, args)));
|
|
19319
|
+
return {
|
|
19320
|
+
args_get: g(this.args_get),
|
|
19321
|
+
args_sizes_get: g(this.args_sizes_get),
|
|
19322
|
+
environ_get: g(this.environ_get),
|
|
19323
|
+
environ_sizes_get: g(this.environ_sizes_get),
|
|
19324
|
+
clock_res_get: g(this.clock_res_get),
|
|
19325
|
+
clock_time_get: g(this.clock_time_get),
|
|
19326
|
+
fd_advise: g(() => WASI_ESUCCESS),
|
|
19327
|
+
fd_allocate: g(this.fd_allocate),
|
|
19328
|
+
fd_close: g(this.fd_close),
|
|
19329
|
+
fd_datasync: g(this.fd_sync),
|
|
19330
|
+
fd_fdstat_get: g(this.fd_fdstat_get),
|
|
19331
|
+
fd_fdstat_set_flags: g(this.fd_fdstat_set_flags),
|
|
19332
|
+
fd_fdstat_set_rights: g(() => WASI_ESUCCESS),
|
|
19333
|
+
fd_filestat_get: g(this.fd_filestat_get),
|
|
19334
|
+
fd_filestat_set_size: g(this.fd_filestat_set_size),
|
|
19335
|
+
fd_filestat_set_times: g(this.fd_filestat_set_times),
|
|
19336
|
+
fd_pread: g(this.fd_pread),
|
|
19337
|
+
fd_prestat_get: g(this.fd_prestat_get),
|
|
19338
|
+
fd_prestat_dir_name: g(this.fd_prestat_dir_name),
|
|
19339
|
+
fd_pwrite: g(this.fd_pwrite),
|
|
19340
|
+
fd_read: g(this.fd_read),
|
|
19341
|
+
fd_readdir: g(this.fd_readdir),
|
|
19342
|
+
fd_renumber: g(this.fd_renumber),
|
|
19343
|
+
fd_seek: g(this.fd_seek),
|
|
19344
|
+
fd_sync: g(this.fd_sync),
|
|
19345
|
+
fd_tell: g(this.fd_tell),
|
|
19346
|
+
fd_write: g(this.fd_write),
|
|
19347
|
+
path_create_directory: g(this.path_create_directory),
|
|
19348
|
+
path_filestat_get: g(this.path_filestat_get),
|
|
19349
|
+
path_filestat_set_times: g(this.path_filestat_set_times),
|
|
19350
|
+
path_link: g(this.path_link),
|
|
19351
|
+
path_open: g(this.path_open),
|
|
19352
|
+
path_readlink: g(this.path_readlink),
|
|
19353
|
+
path_remove_directory: g(this.path_remove_directory),
|
|
19354
|
+
path_rename: g(this.path_rename),
|
|
19355
|
+
path_symlink: g(this.path_symlink),
|
|
19356
|
+
path_unlink_file: g(this.path_unlink_file),
|
|
19357
|
+
poll_oneoff: g(this.poll_oneoff),
|
|
19358
|
+
proc_exit: ((code) => {
|
|
19359
|
+
this.exitCode = code;
|
|
19360
|
+
throw new WasiExit(code);
|
|
19361
|
+
}),
|
|
19362
|
+
proc_raise: g(() => WASI_ENOSYS),
|
|
19363
|
+
random_get: g(this.random_get),
|
|
19364
|
+
sched_yield: g(() => WASI_ESUCCESS),
|
|
19365
|
+
/* Sockets are the one part of WASI this container cannot honour: its
|
|
19366
|
+
* network is an HTTP router, not a packet path. Saying so with ENOTSUP
|
|
19367
|
+
* lets a guest fall back, where a trap would just kill it. */
|
|
19368
|
+
sock_accept: g(() => WASI_ENOTSUP),
|
|
19369
|
+
sock_recv: g(() => WASI_ENOTSUP),
|
|
19370
|
+
sock_send: g(() => WASI_ENOTSUP),
|
|
19371
|
+
sock_shutdown: g(() => WASI_ENOTSUP)
|
|
19372
|
+
};
|
|
19373
|
+
}
|
|
19374
|
+
/**
|
|
19375
|
+
* `wasi_unstable`, the preview0 name older toolchains still emit.
|
|
19376
|
+
*
|
|
19377
|
+
* Identical but for `fd_seek`, whose `whence` values were reordered before
|
|
19378
|
+
* preview1 was frozen. Aliasing the table without this correction is a
|
|
19379
|
+
* popular bug: every seek in an old binary lands somewhere plausible and
|
|
19380
|
+
* wrong.
|
|
19381
|
+
*/
|
|
19382
|
+
get wasiUnstableImport() {
|
|
19383
|
+
const preview0Whence = [WHENCE_CUR, WHENCE_END, WHENCE_SET];
|
|
19384
|
+
return {
|
|
19385
|
+
...this.wasiImport,
|
|
19386
|
+
fd_seek: ((fd, offset, whence, out) => this.guard(
|
|
19387
|
+
() => this.fd_seek(fd, offset, preview0Whence[whence] ?? WHENCE_SET, out)
|
|
19388
|
+
))
|
|
19389
|
+
};
|
|
19390
|
+
}
|
|
19391
|
+
// ── memory access ────────────────────────────────────────────────────────
|
|
19392
|
+
//
|
|
19393
|
+
// Views are built per call rather than cached: `memory.grow` detaches every
|
|
19394
|
+
// existing view, so a cached one turns into a silent zero-length buffer the
|
|
19395
|
+
// first time the guest's allocator asks for another page.
|
|
19396
|
+
get view() {
|
|
19397
|
+
if (!this.memory) throw new Error("wasi: memory is not bound");
|
|
19398
|
+
return new DataView(this.memory.buffer);
|
|
19399
|
+
}
|
|
19400
|
+
get bytes() {
|
|
19401
|
+
if (!this.memory) throw new Error("wasi: memory is not bound");
|
|
19402
|
+
return new Uint8Array(this.memory.buffer);
|
|
19403
|
+
}
|
|
19404
|
+
readString(ptr, len) {
|
|
19405
|
+
return decoder7.decode(this.bytes.subarray(ptr, ptr + len));
|
|
19406
|
+
}
|
|
19407
|
+
/** The scatter/gather list `fd_read` and `fd_write` are given. */
|
|
19408
|
+
iovecs(ptr, count) {
|
|
19409
|
+
const view = this.view;
|
|
19410
|
+
const out = [];
|
|
19411
|
+
for (let i = 0; i < count; i++) {
|
|
19412
|
+
const base2 = ptr + i * 8;
|
|
19413
|
+
out.push({ offset: view.getUint32(base2, true), length: view.getUint32(base2 + 4, true) });
|
|
19414
|
+
}
|
|
19415
|
+
return out;
|
|
19416
|
+
}
|
|
19417
|
+
/**
|
|
19418
|
+
* Turn anything thrown inside a syscall into an errno.
|
|
19419
|
+
*
|
|
19420
|
+
* `WasiExit` is re-thrown on purpose: it is the guest unwinding its own
|
|
19421
|
+
* stack, not a failure to be reported through a return value.
|
|
19422
|
+
*/
|
|
19423
|
+
guard(fn) {
|
|
19424
|
+
try {
|
|
19425
|
+
return fn();
|
|
19426
|
+
} catch (error) {
|
|
19427
|
+
if (error instanceof WasiExit) throw error;
|
|
19428
|
+
return errnoOf(error);
|
|
19429
|
+
}
|
|
19430
|
+
}
|
|
19431
|
+
// ── descriptors ──────────────────────────────────────────────────────────
|
|
19432
|
+
get(fd) {
|
|
19433
|
+
const found = this.fds.get(fd);
|
|
19434
|
+
if (!found) throw new WasiError(WASI_EBADF);
|
|
19435
|
+
return found;
|
|
19436
|
+
}
|
|
19437
|
+
dir(fd) {
|
|
19438
|
+
const found = this.get(fd);
|
|
19439
|
+
if (found.kind !== "dir") throw new WasiError(WASI_ENOTDIR);
|
|
19440
|
+
return found;
|
|
19441
|
+
}
|
|
19442
|
+
file(fd) {
|
|
19443
|
+
const found = this.get(fd);
|
|
19444
|
+
if (found.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19445
|
+
return found;
|
|
19446
|
+
}
|
|
19447
|
+
/**
|
|
19448
|
+
* Resolve a guest path against a directory descriptor.
|
|
19449
|
+
*
|
|
19450
|
+
* The confinement check is the one place preopens are enforced: a path that
|
|
19451
|
+
* climbs out of the directory the descriptor was derived from is
|
|
19452
|
+
* `ENOTCAPABLE`, which is exactly the error a capability-oriented guest
|
|
19453
|
+
* expects and knows how to report.
|
|
19454
|
+
*/
|
|
19455
|
+
resolveAt(dirfd, path) {
|
|
19456
|
+
const dir3 = this.dir(dirfd);
|
|
19457
|
+
if (path === "") throw new WasiError(WASI_ENOENT);
|
|
19458
|
+
const abs = isAbsolute(path) ? resolve("/", path) : resolve(dir3.path, path);
|
|
19459
|
+
const root = dir3.root;
|
|
19460
|
+
if (root !== "/" && abs !== root && !abs.startsWith(root.endsWith("/") ? root : `${root}/`)) {
|
|
19461
|
+
throw new WasiError(WASI_ENOTCAPABLE);
|
|
19462
|
+
}
|
|
19463
|
+
return { abs, root };
|
|
19464
|
+
}
|
|
19465
|
+
allocate(descriptor) {
|
|
19466
|
+
const fd = this.nextFd++;
|
|
19467
|
+
this.fds.set(fd, descriptor);
|
|
19468
|
+
return fd;
|
|
19469
|
+
}
|
|
19470
|
+
/**
|
|
19471
|
+
* Push a buffered file back to the VFS.
|
|
19472
|
+
*
|
|
19473
|
+
* Called before every path-based call as well as on close, so a guest that
|
|
19474
|
+
* writes a file and then stats or reopens it by name sees what it wrote —
|
|
19475
|
+
* the alternative is a stale read that looks like data loss.
|
|
19476
|
+
*/
|
|
19477
|
+
writeBack(fd) {
|
|
19478
|
+
if (!fd.dirty) return;
|
|
19479
|
+
this.opts.vfs.writeFile(fd.path, fd.data.subarray(0, fd.size), { cred: this.opts.cred });
|
|
19480
|
+
fd.dirty = false;
|
|
19481
|
+
}
|
|
19482
|
+
syncPaths() {
|
|
19483
|
+
for (const fd of this.fds.values()) if (fd.kind === "file" && fd.dirty) this.writeBack(fd);
|
|
19484
|
+
}
|
|
19485
|
+
// ── args and environment ─────────────────────────────────────────────────
|
|
19486
|
+
args_get(argvPtr, bufPtr) {
|
|
19487
|
+
return this.writeStringVector(this.opts.argv, argvPtr, bufPtr);
|
|
19488
|
+
}
|
|
19489
|
+
args_sizes_get(countPtr, sizePtr) {
|
|
19490
|
+
return this.writeVectorSizes(this.opts.argv, countPtr, sizePtr);
|
|
19491
|
+
}
|
|
19492
|
+
get envStrings() {
|
|
19493
|
+
return Object.entries(this.opts.env).map(([key, value]) => `${key}=${value}`);
|
|
19494
|
+
}
|
|
19495
|
+
environ_get(envPtr, bufPtr) {
|
|
19496
|
+
return this.writeStringVector(this.envStrings, envPtr, bufPtr);
|
|
19497
|
+
}
|
|
19498
|
+
environ_sizes_get(countPtr, sizePtr) {
|
|
19499
|
+
return this.writeVectorSizes(this.envStrings, countPtr, sizePtr);
|
|
19500
|
+
}
|
|
19501
|
+
writeStringVector(values, pointersPtr, bufPtr) {
|
|
19502
|
+
const view = this.view;
|
|
19503
|
+
const memory = this.bytes;
|
|
19504
|
+
let cursor = bufPtr;
|
|
19505
|
+
values.forEach((value, index) => {
|
|
19506
|
+
view.setUint32(pointersPtr + index * 4, cursor, true);
|
|
19507
|
+
const encoded = encoder6.encode(`${value}\0`);
|
|
19508
|
+
memory.set(encoded, cursor);
|
|
19509
|
+
cursor += encoded.length;
|
|
19510
|
+
});
|
|
19511
|
+
return WASI_ESUCCESS;
|
|
19512
|
+
}
|
|
19513
|
+
writeVectorSizes(values, countPtr, sizePtr) {
|
|
19514
|
+
const view = this.view;
|
|
19515
|
+
view.setUint32(countPtr, values.length, true);
|
|
19516
|
+
view.setUint32(
|
|
19517
|
+
sizePtr,
|
|
19518
|
+
values.reduce((total, value) => total + encoder6.encode(value).length + 1, 0),
|
|
19519
|
+
true
|
|
19520
|
+
);
|
|
19521
|
+
return WASI_ESUCCESS;
|
|
19522
|
+
}
|
|
19523
|
+
// ── clocks and randomness ────────────────────────────────────────────────
|
|
19524
|
+
clock_res_get(_id, out) {
|
|
19525
|
+
this.view.setBigUint64(out, 1000000n, true);
|
|
19526
|
+
return WASI_ESUCCESS;
|
|
19527
|
+
}
|
|
19528
|
+
clock_time_get(id, _precision, out) {
|
|
19529
|
+
this.view.setBigUint64(out, this.clockNow(id), true);
|
|
19530
|
+
return WASI_ESUCCESS;
|
|
19531
|
+
}
|
|
19532
|
+
clockNow(id) {
|
|
19533
|
+
if (id === 0) return BigInt(Math.round(this.opts.now())) * 1000000n;
|
|
19534
|
+
return this.opts.hrtime() - this.startTime;
|
|
19535
|
+
}
|
|
19536
|
+
random_get(ptr, len) {
|
|
19537
|
+
const into = new Uint8Array(len);
|
|
19538
|
+
this.opts.random(into);
|
|
19539
|
+
this.bytes.set(into, ptr);
|
|
19540
|
+
return WASI_ESUCCESS;
|
|
19541
|
+
}
|
|
19542
|
+
// ── reading and writing ──────────────────────────────────────────────────
|
|
19543
|
+
fd_read(fd, iovsPtr, iovsLen, out) {
|
|
19544
|
+
const descriptor = this.get(fd);
|
|
19545
|
+
const iovs = this.iovecs(iovsPtr, iovsLen);
|
|
19546
|
+
let total = 0;
|
|
19547
|
+
if (descriptor.kind === "stdin") {
|
|
19548
|
+
for (const iov of iovs) {
|
|
19549
|
+
if (iov.length === 0) continue;
|
|
19550
|
+
const chunk = this.opts.stdin.read(iov.length);
|
|
19551
|
+
if (chunk.length === 0) break;
|
|
19552
|
+
this.bytes.set(chunk, iov.offset);
|
|
19553
|
+
total += chunk.length;
|
|
19554
|
+
if (chunk.length < iov.length) break;
|
|
19555
|
+
}
|
|
19556
|
+
this.view.setUint32(out, total, true);
|
|
19557
|
+
return WASI_ESUCCESS;
|
|
19558
|
+
}
|
|
19559
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19560
|
+
if ((descriptor.rights & RIGHT_FD_READ) === 0n) throw new WasiError(WASI_EACCES);
|
|
19561
|
+
for (const iov of iovs) {
|
|
19562
|
+
const end = Math.min(descriptor.pos + iov.length, descriptor.size);
|
|
19563
|
+
const chunk = descriptor.data.subarray(descriptor.pos, Math.max(end, descriptor.pos));
|
|
19564
|
+
if (chunk.length === 0) break;
|
|
19565
|
+
this.bytes.set(chunk, iov.offset);
|
|
19566
|
+
descriptor.pos += chunk.length;
|
|
19567
|
+
total += chunk.length;
|
|
19568
|
+
if (chunk.length < iov.length) break;
|
|
19569
|
+
}
|
|
19570
|
+
this.view.setUint32(out, total, true);
|
|
19571
|
+
return WASI_ESUCCESS;
|
|
19572
|
+
}
|
|
19573
|
+
fd_pread(fd, iovsPtr, iovsLen, offset, out) {
|
|
19574
|
+
const descriptor = this.get(fd);
|
|
19575
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19576
|
+
const saved = descriptor.pos;
|
|
19577
|
+
descriptor.pos = Number(offset);
|
|
19578
|
+
try {
|
|
19579
|
+
return this.fd_read(fd, iovsPtr, iovsLen, out);
|
|
19580
|
+
} finally {
|
|
19581
|
+
descriptor.pos = saved;
|
|
19582
|
+
}
|
|
19583
|
+
}
|
|
19584
|
+
fd_write(fd, iovsPtr, iovsLen, out) {
|
|
19585
|
+
const descriptor = this.get(fd);
|
|
19586
|
+
const iovs = this.iovecs(iovsPtr, iovsLen);
|
|
19587
|
+
let total = 0;
|
|
19588
|
+
if (descriptor.kind === "stdout" || descriptor.kind === "stderr") {
|
|
19589
|
+
for (const iov of iovs) {
|
|
19590
|
+
const chunk = this.bytes.slice(iov.offset, iov.offset + iov.length);
|
|
19591
|
+
try {
|
|
19592
|
+
descriptor.stream.write(chunk);
|
|
19593
|
+
} catch (error) {
|
|
19594
|
+
if (isSysError(error) && error.code === "EPIPE") throw new WasiError(WASI_EPIPE);
|
|
19595
|
+
throw error;
|
|
19596
|
+
}
|
|
19597
|
+
total += chunk.length;
|
|
19598
|
+
}
|
|
19599
|
+
this.view.setUint32(out, total, true);
|
|
19600
|
+
return WASI_ESUCCESS;
|
|
19601
|
+
}
|
|
19602
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_EBADF);
|
|
19603
|
+
if ((descriptor.rights & RIGHT_FD_WRITE) === 0n) throw new WasiError(WASI_EACCES);
|
|
19604
|
+
if (descriptor.append) descriptor.pos = descriptor.size;
|
|
19605
|
+
for (const iov of iovs) {
|
|
19606
|
+
const chunk = this.bytes.subarray(iov.offset, iov.offset + iov.length);
|
|
19607
|
+
reserve(descriptor, descriptor.pos + chunk.length);
|
|
19608
|
+
descriptor.data.set(chunk, descriptor.pos);
|
|
19609
|
+
descriptor.pos += chunk.length;
|
|
19610
|
+
descriptor.size = Math.max(descriptor.size, descriptor.pos);
|
|
19611
|
+
total += chunk.length;
|
|
19612
|
+
}
|
|
19613
|
+
descriptor.dirty = true;
|
|
19614
|
+
this.view.setUint32(out, total, true);
|
|
19615
|
+
return WASI_ESUCCESS;
|
|
19616
|
+
}
|
|
19617
|
+
fd_pwrite(fd, iovsPtr, iovsLen, offset, out) {
|
|
19618
|
+
const descriptor = this.get(fd);
|
|
19619
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19620
|
+
const saved = descriptor.pos;
|
|
19621
|
+
const appended = descriptor.append;
|
|
19622
|
+
descriptor.pos = Number(offset);
|
|
19623
|
+
descriptor.append = false;
|
|
19624
|
+
try {
|
|
19625
|
+
return this.fd_write(fd, iovsPtr, iovsLen, out);
|
|
19626
|
+
} finally {
|
|
19627
|
+
descriptor.pos = saved;
|
|
19628
|
+
descriptor.append = appended;
|
|
19629
|
+
}
|
|
19630
|
+
}
|
|
19631
|
+
fd_seek(fd, offset, whence, out) {
|
|
19632
|
+
const descriptor = this.get(fd);
|
|
19633
|
+
if (descriptor.kind !== "file") throw new WasiError(WASI_ESPIPE);
|
|
19634
|
+
if ((descriptor.rights & RIGHT_FD_SEEK) === 0n) throw new WasiError(WASI_EACCES);
|
|
19635
|
+
const base2 = whence === WHENCE_SET ? 0 : whence === WHENCE_CUR ? descriptor.pos : descriptor.size;
|
|
19636
|
+
const next = base2 + Number(offset);
|
|
19637
|
+
if (next < 0) throw new WasiError(WASI_EINVAL);
|
|
19638
|
+
descriptor.pos = next;
|
|
19639
|
+
this.view.setBigUint64(out, BigInt(next), true);
|
|
19640
|
+
return WASI_ESUCCESS;
|
|
19641
|
+
}
|
|
19642
|
+
fd_tell(fd, out) {
|
|
19643
|
+
this.view.setBigUint64(out, BigInt(this.file(fd).pos), true);
|
|
19644
|
+
return WASI_ESUCCESS;
|
|
19645
|
+
}
|
|
19646
|
+
fd_close(fd) {
|
|
19647
|
+
const descriptor = this.get(fd);
|
|
19648
|
+
if (descriptor.kind === "file") this.writeBack(descriptor);
|
|
19649
|
+
this.fds.delete(fd);
|
|
19650
|
+
return WASI_ESUCCESS;
|
|
19651
|
+
}
|
|
19652
|
+
fd_sync(fd) {
|
|
19653
|
+
const descriptor = this.get(fd);
|
|
19654
|
+
if (descriptor.kind === "file") this.writeBack(descriptor);
|
|
19655
|
+
return WASI_ESUCCESS;
|
|
19656
|
+
}
|
|
19657
|
+
fd_renumber(from, to) {
|
|
19658
|
+
const descriptor = this.get(from);
|
|
19659
|
+
const replaced = this.fds.get(to);
|
|
19660
|
+
if (replaced?.kind === "file") this.writeBack(replaced);
|
|
19661
|
+
this.fds.set(to, descriptor);
|
|
19662
|
+
this.fds.delete(from);
|
|
19663
|
+
return WASI_ESUCCESS;
|
|
19664
|
+
}
|
|
19665
|
+
fd_allocate(fd, offset, len) {
|
|
19666
|
+
const descriptor = this.file(fd);
|
|
19667
|
+
const size = Number(offset + len);
|
|
19668
|
+
if (descriptor.size < size) {
|
|
19669
|
+
reserve(descriptor, size);
|
|
19670
|
+
descriptor.data.fill(0, descriptor.size, size);
|
|
19671
|
+
descriptor.size = size;
|
|
19672
|
+
descriptor.dirty = true;
|
|
19673
|
+
}
|
|
19674
|
+
return WASI_ESUCCESS;
|
|
19675
|
+
}
|
|
19676
|
+
fd_filestat_set_size(fd, size) {
|
|
19677
|
+
const descriptor = this.file(fd);
|
|
19678
|
+
const length = Number(size);
|
|
19679
|
+
reserve(descriptor, length);
|
|
19680
|
+
if (length > descriptor.size) descriptor.data.fill(0, descriptor.size, length);
|
|
19681
|
+
descriptor.size = length;
|
|
19682
|
+
if (descriptor.pos > length) descriptor.pos = length;
|
|
19683
|
+
descriptor.dirty = true;
|
|
19684
|
+
return WASI_ESUCCESS;
|
|
19685
|
+
}
|
|
19686
|
+
// ── metadata ─────────────────────────────────────────────────────────────
|
|
19687
|
+
fd_fdstat_get(fd, out) {
|
|
19688
|
+
const descriptor = this.get(fd);
|
|
19689
|
+
const view = this.view;
|
|
19690
|
+
const [filetype, flags, rights] = (() => {
|
|
19691
|
+
switch (descriptor.kind) {
|
|
19692
|
+
case "dir":
|
|
19693
|
+
return [FILETYPE_DIRECTORY, 0, descriptor.rights];
|
|
19694
|
+
case "file":
|
|
19695
|
+
return [FILETYPE_REGULAR_FILE, descriptor.flags, descriptor.rights];
|
|
19696
|
+
default:
|
|
19697
|
+
return [FILETYPE_CHARACTER_DEVICE, 0, RIGHTS_ALL];
|
|
19698
|
+
}
|
|
19699
|
+
})();
|
|
19700
|
+
zero(this.bytes, out, SIZEOF_FDSTAT);
|
|
19701
|
+
view.setUint8(out, filetype);
|
|
19702
|
+
view.setUint16(out + 2, flags, true);
|
|
19703
|
+
view.setBigUint64(out + 8, rights, true);
|
|
19704
|
+
view.setBigUint64(out + 16, rights, true);
|
|
19705
|
+
return WASI_ESUCCESS;
|
|
19706
|
+
}
|
|
19707
|
+
fd_fdstat_set_flags(fd, flags) {
|
|
19708
|
+
const descriptor = this.file(fd);
|
|
19709
|
+
descriptor.flags = flags;
|
|
19710
|
+
descriptor.append = (flags & FD_APPEND) !== 0;
|
|
19711
|
+
return WASI_ESUCCESS;
|
|
19712
|
+
}
|
|
19713
|
+
fd_filestat_get(fd, out) {
|
|
19714
|
+
const descriptor = this.get(fd);
|
|
19715
|
+
if (descriptor.kind === "file" || descriptor.kind === "dir") {
|
|
19716
|
+
this.syncPaths();
|
|
19717
|
+
this.writeFilestat(out, this.opts.vfs.stat(descriptor.path, { cred: this.opts.cred }));
|
|
19718
|
+
return WASI_ESUCCESS;
|
|
19719
|
+
}
|
|
19720
|
+
zero(this.bytes, out, SIZEOF_FILESTAT);
|
|
19721
|
+
this.view.setUint8(out + 16, FILETYPE_CHARACTER_DEVICE);
|
|
19722
|
+
return WASI_ESUCCESS;
|
|
19723
|
+
}
|
|
19724
|
+
fd_filestat_set_times(fd, atim, mtim, flags) {
|
|
19725
|
+
const descriptor = this.get(fd);
|
|
19726
|
+
if (descriptor.kind !== "file" && descriptor.kind !== "dir") return WASI_ESUCCESS;
|
|
19727
|
+
return this.setTimes(descriptor.path, atim, mtim, flags);
|
|
19728
|
+
}
|
|
19729
|
+
path_filestat_get(dirfd, lookupFlags, pathPtr, pathLen, out) {
|
|
19730
|
+
this.syncPaths();
|
|
19731
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19732
|
+
const follow = (lookupFlags & LOOKUP_SYMLINK_FOLLOW) !== 0;
|
|
19733
|
+
this.writeFilestat(
|
|
19734
|
+
out,
|
|
19735
|
+
follow ? this.opts.vfs.stat(abs, { cred: this.opts.cred }) : this.opts.vfs.lstat(abs)
|
|
19736
|
+
);
|
|
19737
|
+
return WASI_ESUCCESS;
|
|
19738
|
+
}
|
|
19739
|
+
path_filestat_set_times(dirfd, _lookupFlags, pathPtr, pathLen, atim, mtim, flags) {
|
|
19740
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19741
|
+
return this.setTimes(abs, atim, mtim, flags);
|
|
19742
|
+
}
|
|
19743
|
+
setTimes(path, atim, mtim, flags) {
|
|
19744
|
+
const current = this.opts.vfs.stat(path, { cred: this.opts.cred });
|
|
19745
|
+
const now = this.opts.now();
|
|
19746
|
+
const atime = flags & FST_ATIM_NOW ? now : flags & FST_ATIM ? Number(atim / 1000000n) : current.atimeMs;
|
|
19747
|
+
const mtime = flags & FST_MTIM_NOW ? now : flags & FST_MTIM ? Number(mtim / 1000000n) : current.mtimeMs;
|
|
19748
|
+
this.opts.vfs.utimes(path, atime, mtime, this.opts.cred);
|
|
19749
|
+
return WASI_ESUCCESS;
|
|
19750
|
+
}
|
|
19751
|
+
writeFilestat(out, stat2) {
|
|
19752
|
+
const view = this.view;
|
|
19753
|
+
zero(this.bytes, out, SIZEOF_FILESTAT);
|
|
19754
|
+
view.setBigUint64(out, BigInt(stat2.dev), true);
|
|
19755
|
+
view.setBigUint64(out + 8, BigInt(stat2.ino), true);
|
|
19756
|
+
view.setUint8(out + 16, filetypeOf(stat2.mode));
|
|
19757
|
+
view.setBigUint64(out + 24, BigInt(stat2.nlink), true);
|
|
19758
|
+
view.setBigUint64(out + 32, BigInt(stat2.size), true);
|
|
19759
|
+
view.setBigUint64(out + 40, msToNs(stat2.atimeMs), true);
|
|
19760
|
+
view.setBigUint64(out + 48, msToNs(stat2.mtimeMs), true);
|
|
19761
|
+
view.setBigUint64(out + 56, msToNs(stat2.ctimeMs), true);
|
|
19762
|
+
}
|
|
19763
|
+
// ── preopens ─────────────────────────────────────────────────────────────
|
|
19764
|
+
fd_prestat_get(fd, out) {
|
|
19765
|
+
const descriptor = this.fds.get(fd);
|
|
19766
|
+
if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
|
|
19767
|
+
throw new WasiError(WASI_EBADF);
|
|
19768
|
+
}
|
|
19769
|
+
const view = this.view;
|
|
19770
|
+
view.setUint8(
|
|
19771
|
+
out,
|
|
19772
|
+
0
|
|
19773
|
+
/* preopentype: dir */
|
|
19774
|
+
);
|
|
19775
|
+
view.setUint32(out + 4, encoder6.encode(descriptor.preopen).length, true);
|
|
19776
|
+
return WASI_ESUCCESS;
|
|
19777
|
+
}
|
|
19778
|
+
fd_prestat_dir_name(fd, ptr, len) {
|
|
19779
|
+
const descriptor = this.fds.get(fd);
|
|
19780
|
+
if (!descriptor || descriptor.kind !== "dir" || descriptor.preopen === void 0) {
|
|
19781
|
+
throw new WasiError(WASI_EBADF);
|
|
19782
|
+
}
|
|
19783
|
+
const name = encoder6.encode(descriptor.preopen);
|
|
19784
|
+
if (name.length > len) throw new WasiError(WASI_EINVAL);
|
|
19785
|
+
this.bytes.set(name, ptr);
|
|
19786
|
+
return WASI_ESUCCESS;
|
|
19787
|
+
}
|
|
19788
|
+
// ── paths ────────────────────────────────────────────────────────────────
|
|
19789
|
+
path_open(dirfd, _lookupFlags, pathPtr, pathLen, oflags, rightsBase, _rightsInheriting, fdflags, out) {
|
|
19790
|
+
this.syncPaths();
|
|
19791
|
+
const { abs, root } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19792
|
+
const vfs = this.opts.vfs;
|
|
19793
|
+
const cred = this.opts.cred;
|
|
19794
|
+
const exists = vfs.lexists(abs);
|
|
19795
|
+
if (oflags & O_EXCL && oflags & O_CREAT && exists) throw new WasiError(WASI_EEXIST);
|
|
19796
|
+
if (exists && vfs.stat(abs, { cred }).isDirectory()) {
|
|
19797
|
+
if (rightsBase & RIGHT_FD_WRITE) throw new WasiError(WASI_EISDIR);
|
|
19798
|
+
this.view.setUint32(out, this.allocate({ kind: "dir", path: abs, root, rights: RIGHTS_ALL }), true);
|
|
19799
|
+
return WASI_ESUCCESS;
|
|
19800
|
+
}
|
|
19801
|
+
if (oflags & O_DIRECTORY) throw new WasiError(exists ? WASI_ENOTDIR : WASI_ENOENT);
|
|
19802
|
+
if (!exists) {
|
|
19803
|
+
if (!(oflags & O_CREAT)) throw new WasiError(WASI_ENOENT);
|
|
19804
|
+
vfs.writeFile(abs, new Uint8Array(0), { cred, mode: 420 });
|
|
19805
|
+
}
|
|
19806
|
+
const truncate = (oflags & O_TRUNC) !== 0;
|
|
19807
|
+
const data = truncate ? new Uint8Array(0) : vfs.readFile(abs, cred);
|
|
19808
|
+
if (truncate) vfs.writeFile(abs, data, { cred });
|
|
19809
|
+
const size = data.length;
|
|
19810
|
+
const rights = rightsBase === 0n ? RIGHTS_ALL : rightsBase;
|
|
19811
|
+
const append = (fdflags & FD_APPEND) !== 0;
|
|
19812
|
+
this.view.setUint32(
|
|
19813
|
+
out,
|
|
19814
|
+
this.allocate({
|
|
19815
|
+
kind: "file",
|
|
19816
|
+
path: abs,
|
|
19817
|
+
root,
|
|
19818
|
+
data,
|
|
19819
|
+
size,
|
|
19820
|
+
pos: append ? size : 0,
|
|
19821
|
+
dirty: false,
|
|
19822
|
+
append,
|
|
19823
|
+
rights,
|
|
19824
|
+
flags: fdflags
|
|
19825
|
+
}),
|
|
19826
|
+
true
|
|
19827
|
+
);
|
|
19828
|
+
return WASI_ESUCCESS;
|
|
19829
|
+
}
|
|
19830
|
+
fd_readdir(fd, buf, bufLen, cookie, out) {
|
|
19831
|
+
this.syncPaths();
|
|
19832
|
+
const descriptor = this.dir(fd);
|
|
19833
|
+
const vfs = this.opts.vfs;
|
|
19834
|
+
const cred = this.opts.cred;
|
|
19835
|
+
const entries = [
|
|
19836
|
+
{ name: ".", type: FILETYPE_DIRECTORY },
|
|
19837
|
+
{ name: "..", type: FILETYPE_DIRECTORY },
|
|
19838
|
+
...vfs.readdirWithTypes(descriptor.path, cred).map((entry) => ({
|
|
19839
|
+
name: entry.name,
|
|
19840
|
+
type: entry.kind === "directory" ? FILETYPE_DIRECTORY : entry.kind === "symlink" ? FILETYPE_SYMBOLIC_LINK : entry.kind === "file" ? FILETYPE_REGULAR_FILE : FILETYPE_UNKNOWN
|
|
19841
|
+
}))
|
|
19842
|
+
];
|
|
19843
|
+
const view = this.view;
|
|
19844
|
+
const memory = this.bytes;
|
|
19845
|
+
let written = 0;
|
|
19846
|
+
for (let index = Number(cookie); index < entries.length; index++) {
|
|
19847
|
+
const entry = entries[index];
|
|
19848
|
+
const name = encoder6.encode(entry.name);
|
|
19849
|
+
if (written + SIZEOF_DIRENT > bufLen) break;
|
|
19850
|
+
const at = buf + written;
|
|
19851
|
+
view.setBigUint64(at, BigInt(index + 1), true);
|
|
19852
|
+
view.setBigUint64(at + 8, BigInt(inodeOf(descriptor.path, entry.name)), true);
|
|
19853
|
+
view.setUint32(at + 16, name.length, true);
|
|
19854
|
+
view.setUint8(at + 20, entry.type);
|
|
19855
|
+
written += SIZEOF_DIRENT;
|
|
19856
|
+
const room = Math.min(name.length, bufLen - written);
|
|
19857
|
+
memory.set(name.subarray(0, room), buf + written);
|
|
19858
|
+
written += room;
|
|
19859
|
+
if (room < name.length) break;
|
|
19860
|
+
}
|
|
19861
|
+
view.setUint32(out, written, true);
|
|
19862
|
+
return WASI_ESUCCESS;
|
|
19863
|
+
}
|
|
19864
|
+
path_create_directory(dirfd, pathPtr, pathLen) {
|
|
19865
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19866
|
+
this.opts.vfs.mkdir(abs, { cred: this.opts.cred, mode: 493 });
|
|
19867
|
+
return WASI_ESUCCESS;
|
|
19868
|
+
}
|
|
19869
|
+
path_remove_directory(dirfd, pathPtr, pathLen) {
|
|
19870
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19871
|
+
this.opts.vfs.rmdir(abs, this.opts.cred);
|
|
19872
|
+
return WASI_ESUCCESS;
|
|
19873
|
+
}
|
|
19874
|
+
path_unlink_file(dirfd, pathPtr, pathLen) {
|
|
19875
|
+
this.syncPaths();
|
|
19876
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19877
|
+
this.opts.vfs.unlink(abs, this.opts.cred);
|
|
19878
|
+
return WASI_ESUCCESS;
|
|
19879
|
+
}
|
|
19880
|
+
path_rename(oldDirfd, oldPtr, oldLen, newDirfd, newPtr, newLen) {
|
|
19881
|
+
this.syncPaths();
|
|
19882
|
+
const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
|
|
19883
|
+
const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
|
|
19884
|
+
this.opts.vfs.rename(from.abs, to.abs, this.opts.cred);
|
|
19885
|
+
return WASI_ESUCCESS;
|
|
19886
|
+
}
|
|
19887
|
+
path_symlink(targetPtr, targetLen, dirfd, pathPtr, pathLen) {
|
|
19888
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19889
|
+
this.opts.vfs.symlink(this.readString(targetPtr, targetLen), abs, this.opts.cred);
|
|
19890
|
+
return WASI_ESUCCESS;
|
|
19891
|
+
}
|
|
19892
|
+
path_link(oldDirfd, _lookupFlags, oldPtr, oldLen, newDirfd, newPtr, newLen) {
|
|
19893
|
+
this.syncPaths();
|
|
19894
|
+
const from = this.resolveAt(oldDirfd, this.readString(oldPtr, oldLen));
|
|
19895
|
+
const to = this.resolveAt(newDirfd, this.readString(newPtr, newLen));
|
|
19896
|
+
this.opts.vfs.link(from.abs, to.abs, this.opts.cred);
|
|
19897
|
+
return WASI_ESUCCESS;
|
|
19898
|
+
}
|
|
19899
|
+
path_readlink(dirfd, pathPtr, pathLen, buf, bufLen, out) {
|
|
19900
|
+
const { abs } = this.resolveAt(dirfd, this.readString(pathPtr, pathLen));
|
|
19901
|
+
const target = encoder6.encode(this.opts.vfs.readlink(abs, this.opts.cred));
|
|
19902
|
+
const room = Math.min(target.length, bufLen);
|
|
19903
|
+
this.bytes.set(target.subarray(0, room), buf);
|
|
19904
|
+
this.view.setUint32(out, room, true);
|
|
19905
|
+
return WASI_ESUCCESS;
|
|
19906
|
+
}
|
|
19907
|
+
// ── polling ──────────────────────────────────────────────────────────────
|
|
19908
|
+
/**
|
|
19909
|
+
* `poll_oneoff`, which is how a WASI guest sleeps and how it waits on I/O.
|
|
19910
|
+
*
|
|
19911
|
+
* Files and the standard streams are always ready here — nothing in this
|
|
19912
|
+
* container can leave a read pending, since stdin was drained before the
|
|
19913
|
+
* guest started. That leaves the clock, which is the case that matters:
|
|
19914
|
+
* `sleep()` compiles to a lone clock subscription, and it is honoured by
|
|
19915
|
+
* actually waiting, in slices, so that killing the process interrupts it.
|
|
19916
|
+
*/
|
|
19917
|
+
poll_oneoff(inPtr, outPtr, count, nevents) {
|
|
19918
|
+
if (count === 0) {
|
|
19919
|
+
this.view.setUint32(nevents, 0, true);
|
|
19920
|
+
return WASI_ESUCCESS;
|
|
19921
|
+
}
|
|
19922
|
+
const subscriptions = [];
|
|
19923
|
+
for (let index = 0; index < count; index++) {
|
|
19924
|
+
const at = inPtr + index * SIZEOF_SUBSCRIPTION;
|
|
19925
|
+
const view2 = this.view;
|
|
19926
|
+
subscriptions.push({
|
|
19927
|
+
userdata: view2.getBigUint64(at, true),
|
|
19928
|
+
type: view2.getUint8(at + 8),
|
|
19929
|
+
fd: view2.getUint32(at + 16, true),
|
|
19930
|
+
clockId: view2.getUint32(at + 16, true),
|
|
19931
|
+
timeout: view2.getBigUint64(at + 24, true),
|
|
19932
|
+
flags: view2.getUint16(at + 40, true)
|
|
19933
|
+
});
|
|
19934
|
+
}
|
|
19935
|
+
const ready = subscriptions.filter((s) => s.type !== EVENTTYPE_CLOCK);
|
|
19936
|
+
if (ready.length === 0) {
|
|
19937
|
+
const delays = subscriptions.map((s) => {
|
|
19938
|
+
const nanoseconds = (s.flags & SUBSCRIPTION_CLOCK_ABSTIME) !== 0 ? s.timeout - this.clockNow(s.clockId) : s.timeout;
|
|
19939
|
+
return Number(nanoseconds < 0n ? 0n : nanoseconds) / 1e6;
|
|
19940
|
+
});
|
|
19941
|
+
this.sleepInterruptibly(Math.min(...delays));
|
|
19942
|
+
}
|
|
19943
|
+
const events = ready.length > 0 ? ready : subscriptions;
|
|
19944
|
+
const view = this.view;
|
|
19945
|
+
events.forEach((subscription, index) => {
|
|
19946
|
+
const at = outPtr + index * SIZEOF_EVENT;
|
|
19947
|
+
zero(this.bytes, at, SIZEOF_EVENT);
|
|
19948
|
+
view.setBigUint64(at, subscription.userdata, true);
|
|
19949
|
+
view.setUint16(at + 8, WASI_ESUCCESS, true);
|
|
19950
|
+
view.setUint8(at + 10, subscription.type);
|
|
19951
|
+
if (subscription.type === EVENTTYPE_FD_READ) {
|
|
19952
|
+
const available = subscription.fd === 0 ? this.opts.stdin.available : this.bytesLeft(subscription.fd);
|
|
19953
|
+
view.setBigUint64(at + 16, BigInt(available), true);
|
|
19954
|
+
} else if (subscription.type === EVENTTYPE_FD_WRITE) {
|
|
19955
|
+
view.setBigUint64(at + 16, BigInt(64 * 1024), true);
|
|
19956
|
+
}
|
|
19957
|
+
});
|
|
19958
|
+
view.setUint32(nevents, events.length, true);
|
|
19959
|
+
return WASI_ESUCCESS;
|
|
19960
|
+
}
|
|
19961
|
+
bytesLeft(fd) {
|
|
19962
|
+
const descriptor = this.fds.get(fd);
|
|
19963
|
+
return descriptor?.kind === "file" ? Math.max(0, descriptor.size - descriptor.pos) : 0;
|
|
19964
|
+
}
|
|
19965
|
+
/** Wait in slices so that a `SIGKILL` does not have to outlast the sleep. */
|
|
19966
|
+
sleepInterruptibly(milliseconds) {
|
|
19967
|
+
let remaining = milliseconds;
|
|
19968
|
+
while (remaining > 0) {
|
|
19969
|
+
if (this.opts.aborted()) return;
|
|
19970
|
+
const slice = Math.min(remaining, 50);
|
|
19971
|
+
this.opts.sleep(slice);
|
|
19972
|
+
remaining -= slice;
|
|
19973
|
+
}
|
|
19974
|
+
}
|
|
19975
|
+
};
|
|
19976
|
+
var WasiError = class extends Error {
|
|
19977
|
+
constructor(errno) {
|
|
19978
|
+
super(`wasi errno ${errno}`);
|
|
19979
|
+
this.errno = errno;
|
|
19980
|
+
this.name = "WasiError";
|
|
19981
|
+
}
|
|
19982
|
+
errno;
|
|
19983
|
+
};
|
|
19984
|
+
function errnoOf(error) {
|
|
19985
|
+
if (error instanceof WasiError) return error.errno;
|
|
19986
|
+
if (isSysError(error)) return WASI_ERRNO_BY_CODE[error.code] ?? WASI_EIO;
|
|
19987
|
+
if (error instanceof RangeError) return WASI_EFAULT;
|
|
19988
|
+
return WASI_EIO;
|
|
19989
|
+
}
|
|
19990
|
+
function filetypeOf(mode) {
|
|
19991
|
+
const kind = mode & 61440;
|
|
19992
|
+
if (kind === S_IFDIR) return FILETYPE_DIRECTORY;
|
|
19993
|
+
if (kind === S_IFREG) return FILETYPE_REGULAR_FILE;
|
|
19994
|
+
if (kind === S_IFLNK) return FILETYPE_SYMBOLIC_LINK;
|
|
19995
|
+
if (kind === S_IFCHR) return FILETYPE_CHARACTER_DEVICE;
|
|
19996
|
+
return FILETYPE_UNKNOWN;
|
|
19997
|
+
}
|
|
19998
|
+
function msToNs(milliseconds) {
|
|
19999
|
+
return BigInt(Math.round(milliseconds)) * 1000000n;
|
|
20000
|
+
}
|
|
20001
|
+
function zero(memory, at, length) {
|
|
20002
|
+
memory.fill(0, at, at + length);
|
|
20003
|
+
}
|
|
20004
|
+
function reserve(descriptor, length) {
|
|
20005
|
+
if (length <= descriptor.data.length) return;
|
|
20006
|
+
const grown = new Uint8Array(Math.max(length, descriptor.data.length * 2, 1024));
|
|
20007
|
+
grown.set(descriptor.data.subarray(0, descriptor.size));
|
|
20008
|
+
descriptor.data = grown;
|
|
20009
|
+
}
|
|
20010
|
+
function inodeOf(dir3, name) {
|
|
20011
|
+
let hash = 2166136261;
|
|
20012
|
+
const path = `${dir3}/${name}`;
|
|
20013
|
+
for (let index = 0; index < path.length; index++) {
|
|
20014
|
+
hash ^= path.charCodeAt(index);
|
|
20015
|
+
hash = Math.imul(hash, 16777619);
|
|
20016
|
+
}
|
|
20017
|
+
return hash >>> 0;
|
|
20018
|
+
}
|
|
20019
|
+
function defaultHrtime() {
|
|
20020
|
+
return BigInt(Math.round(performance.now() * 1e6));
|
|
20021
|
+
}
|
|
20022
|
+
function defaultRandom(into) {
|
|
20023
|
+
crypto.getRandomValues(into);
|
|
20024
|
+
}
|
|
20025
|
+
function defaultSleep(milliseconds) {
|
|
20026
|
+
try {
|
|
20027
|
+
const buffer = new Int32Array(new SharedArrayBuffer(4));
|
|
20028
|
+
Atomics.wait(buffer, 0, 0, milliseconds);
|
|
20029
|
+
} catch {
|
|
20030
|
+
}
|
|
20031
|
+
}
|
|
20032
|
+
|
|
20033
|
+
// src/wasi/run.ts
|
|
20034
|
+
var compiled2 = /* @__PURE__ */ new Map();
|
|
20035
|
+
var MAX_CACHED_MODULES = 32;
|
|
20036
|
+
async function runWasi(ctx, bytes2, options = {}) {
|
|
20037
|
+
if (!isWasmBinary(bytes2)) {
|
|
20038
|
+
ctx.warn("cannot execute binary file: not a WebAssembly module");
|
|
20039
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20040
|
+
}
|
|
20041
|
+
const argv = options.argv ?? ctx.argv;
|
|
20042
|
+
const host2 = new WasiHost({
|
|
20043
|
+
argv,
|
|
20044
|
+
/* `PWD` is set truthfully, but do not mistake it for a working directory:
|
|
20045
|
+
* wasi-libc keeps its own cwd, fixed at `/`, and ignores this. A guest's
|
|
20046
|
+
* relative path therefore resolves from the root — the same thing it does
|
|
20047
|
+
* under wasmtime, and why programs run here are given absolute paths. */
|
|
20048
|
+
env: { ...ctx.env, PWD: ctx.cwd },
|
|
20049
|
+
vfs: ctx.vfs,
|
|
20050
|
+
cred: ctx.cred,
|
|
20051
|
+
cwd: ctx.cwd,
|
|
20052
|
+
stdin: await primeStdin(ctx),
|
|
20053
|
+
stdout: ctx.stdout,
|
|
20054
|
+
stderr: ctx.stderr,
|
|
20055
|
+
...options.preopens ? { preopens: options.preopens } : {},
|
|
20056
|
+
now: ctx.kernel.now,
|
|
20057
|
+
aborted: () => ctx.signal.aborted
|
|
20058
|
+
});
|
|
20059
|
+
let instance;
|
|
20060
|
+
try {
|
|
20061
|
+
const module = await compile(bytes2);
|
|
20062
|
+
instance = await WebAssembly.instantiate(module, {
|
|
20063
|
+
...options.imports,
|
|
20064
|
+
wasi_snapshot_preview1: host2.wasiImport,
|
|
20065
|
+
wasi_unstable: host2.wasiUnstableImport
|
|
20066
|
+
});
|
|
20067
|
+
} catch (error) {
|
|
20068
|
+
ctx.warn(describeStartupFailure(error));
|
|
20069
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20070
|
+
}
|
|
20071
|
+
try {
|
|
20072
|
+
host2.bind(instance);
|
|
20073
|
+
} catch (error) {
|
|
20074
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
20075
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20076
|
+
}
|
|
20077
|
+
const start2 = instance.exports._start ?? instance.exports._initialize;
|
|
20078
|
+
if (typeof start2 !== "function") {
|
|
20079
|
+
ctx.warn("not a WASI command: the module exports neither _start nor _initialize");
|
|
20080
|
+
return EXIT_NOT_EXECUTABLE;
|
|
20081
|
+
}
|
|
20082
|
+
try {
|
|
20083
|
+
start2();
|
|
20084
|
+
return host2.exitCode ?? 0;
|
|
20085
|
+
} catch (error) {
|
|
20086
|
+
if (error instanceof WasiExit) return error.code;
|
|
20087
|
+
ctx.warn(error instanceof Error ? error.message : String(error));
|
|
20088
|
+
return 134;
|
|
20089
|
+
} finally {
|
|
20090
|
+
try {
|
|
20091
|
+
host2.flushAll();
|
|
20092
|
+
} catch (error) {
|
|
20093
|
+
ctx.warn(`failed to flush open files: ${error instanceof Error ? error.message : String(error)}`);
|
|
20094
|
+
}
|
|
20095
|
+
}
|
|
20096
|
+
}
|
|
20097
|
+
async function compile(bytes2) {
|
|
20098
|
+
const key = await digest(bytes2);
|
|
20099
|
+
const hit = compiled2.get(key);
|
|
20100
|
+
if (hit) {
|
|
20101
|
+
compiled2.delete(key);
|
|
20102
|
+
compiled2.set(key, hit);
|
|
20103
|
+
return hit;
|
|
20104
|
+
}
|
|
20105
|
+
const module = await WebAssembly.compile(sliceForWasm(bytes2));
|
|
20106
|
+
compiled2.set(key, module);
|
|
20107
|
+
if (compiled2.size > MAX_CACHED_MODULES) {
|
|
20108
|
+
const oldest = compiled2.keys().next().value;
|
|
20109
|
+
if (oldest !== void 0) compiled2.delete(oldest);
|
|
20110
|
+
}
|
|
20111
|
+
return module;
|
|
20112
|
+
}
|
|
20113
|
+
async function digest(bytes2) {
|
|
20114
|
+
const hash = await crypto.subtle.digest("SHA-256", sliceForWasm(bytes2));
|
|
20115
|
+
return Array.from(new Uint8Array(hash, 0, 16), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
20116
|
+
}
|
|
20117
|
+
function sliceForWasm(bytes2) {
|
|
20118
|
+
return bytes2.buffer.slice(bytes2.byteOffset, bytes2.byteOffset + bytes2.byteLength);
|
|
20119
|
+
}
|
|
20120
|
+
async function primeStdin(ctx) {
|
|
20121
|
+
let pending = new Uint8Array(0);
|
|
20122
|
+
if (!ctx.stdin.isTTY) {
|
|
20123
|
+
try {
|
|
20124
|
+
pending = await ctx.stdin.readAll();
|
|
20125
|
+
} catch {
|
|
20126
|
+
pending = new Uint8Array(0);
|
|
20127
|
+
}
|
|
20128
|
+
}
|
|
20129
|
+
return {
|
|
20130
|
+
read(size) {
|
|
20131
|
+
const chunk = pending.slice(0, Math.min(size, pending.length));
|
|
20132
|
+
pending = pending.subarray(chunk.length);
|
|
20133
|
+
return chunk;
|
|
20134
|
+
},
|
|
20135
|
+
get available() {
|
|
20136
|
+
return pending.length;
|
|
20137
|
+
},
|
|
20138
|
+
isTTY: ctx.stdin.isTTY
|
|
20139
|
+
};
|
|
20140
|
+
}
|
|
20141
|
+
function describeStartupFailure(error) {
|
|
20142
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20143
|
+
if (error instanceof WebAssembly.LinkError) {
|
|
20144
|
+
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).`;
|
|
20145
|
+
}
|
|
20146
|
+
return message;
|
|
20147
|
+
}
|
|
20148
|
+
|
|
20149
|
+
// src/wasi/command.ts
|
|
20150
|
+
var wasi = defineCommand({
|
|
20151
|
+
name: "wasi",
|
|
20152
|
+
path: "/usr/bin/wasi",
|
|
20153
|
+
summary: "run a WebAssembly (wasm32-wasi) binary",
|
|
20154
|
+
usage: "wasi [--dir PATH] [--mapdir GUEST=PATH] [--env K=V] MODULE.wasm [args...]",
|
|
20155
|
+
manual: `Runs a WebAssembly module built for wasm32-wasi as a container
|
|
20156
|
+
process: it sees the container's filesystem, environment and standard streams,
|
|
20157
|
+
and can be piped and redirected like any other command.
|
|
20158
|
+
|
|
20159
|
+
By default the module is given the whole filesystem, which is what an ordinary
|
|
20160
|
+
program compiled by clang, Rust, Zig or TinyGo expects. Passing --dir or
|
|
20161
|
+
--mapdir replaces that default with exactly what is listed, confining the
|
|
20162
|
+
module to those directories.
|
|
20163
|
+
|
|
20164
|
+
A WASI program has no working directory: wasi-libc fixes its own at / and
|
|
20165
|
+
resolves relative paths from there, exactly as it does under wasmtime. Pass
|
|
20166
|
+
absolute paths, or preopen a directory with --mapdir to give the program a
|
|
20167
|
+
name for it.
|
|
20168
|
+
|
|
20169
|
+
Executable .wasm files are dispatched here automatically, so ./tool.wasm runs
|
|
20170
|
+
without naming this command.
|
|
20171
|
+
|
|
20172
|
+
Sockets are not available: this container's network is an HTTP router rather
|
|
20173
|
+
than a packet path, and socket calls report ENOTSUP.`,
|
|
20174
|
+
async run(ctx) {
|
|
20175
|
+
const preopens = {};
|
|
20176
|
+
const env2 = {};
|
|
20177
|
+
let module;
|
|
20178
|
+
let rest = [];
|
|
20179
|
+
const args = ctx.args;
|
|
20180
|
+
for (let index = 0; index < args.length; index++) {
|
|
20181
|
+
const arg = args[index];
|
|
20182
|
+
if (arg === "--help" || arg === "-h") {
|
|
20183
|
+
ctx.line(wasi.usage);
|
|
20184
|
+
return 0;
|
|
20185
|
+
}
|
|
20186
|
+
if (arg === "--") {
|
|
20187
|
+
module = args[index + 1];
|
|
20188
|
+
rest = args.slice(index + 2);
|
|
20189
|
+
break;
|
|
20190
|
+
}
|
|
20191
|
+
if (arg === "--dir") {
|
|
20192
|
+
const dir3 = args[++index];
|
|
20193
|
+
if (dir3 === void 0) return ctx.reportError(new UsageError("--dir needs a path"));
|
|
20194
|
+
preopens[ctx.path(dir3)] = ctx.path(dir3);
|
|
20195
|
+
continue;
|
|
20196
|
+
}
|
|
20197
|
+
if (arg === "--mapdir") {
|
|
20198
|
+
const pair = args[++index];
|
|
20199
|
+
const split2 = pair?.indexOf("=") ?? -1;
|
|
20200
|
+
if (pair === void 0 || split2 < 0) {
|
|
20201
|
+
return ctx.reportError(new UsageError("--mapdir needs GUEST=PATH"));
|
|
20202
|
+
}
|
|
20203
|
+
preopens[pair.slice(0, split2)] = ctx.path(pair.slice(split2 + 1));
|
|
20204
|
+
continue;
|
|
20205
|
+
}
|
|
20206
|
+
if (arg === "--env") {
|
|
20207
|
+
const pair = args[++index];
|
|
20208
|
+
const split2 = pair?.indexOf("=") ?? -1;
|
|
20209
|
+
if (pair === void 0 || split2 < 0) {
|
|
20210
|
+
return ctx.reportError(new UsageError("--env needs KEY=VALUE"));
|
|
20211
|
+
}
|
|
20212
|
+
env2[pair.slice(0, split2)] = pair.slice(split2 + 1);
|
|
20213
|
+
continue;
|
|
20214
|
+
}
|
|
20215
|
+
module = arg;
|
|
20216
|
+
rest = args.slice(index + 1);
|
|
20217
|
+
break;
|
|
20218
|
+
}
|
|
20219
|
+
if (module === void 0) return ctx.reportError(new UsageError("a module is required"));
|
|
20220
|
+
const abs = ctx.path(module);
|
|
20221
|
+
let bytes2;
|
|
20222
|
+
try {
|
|
20223
|
+
bytes2 = ctx.vfs.readFile(abs, ctx.cred);
|
|
20224
|
+
} catch (error) {
|
|
20225
|
+
ctx.reportError(error, abs);
|
|
20226
|
+
return EXIT_NOT_FOUND;
|
|
20227
|
+
}
|
|
20228
|
+
if (!isWasmBinary(bytes2)) {
|
|
20229
|
+
return ctx.fail(`${abs}: not a WebAssembly module`, 126);
|
|
20230
|
+
}
|
|
20231
|
+
Object.assign(ctx.env, env2);
|
|
20232
|
+
return await runWasi(ctx, bytes2, {
|
|
20233
|
+
/* argv[0] is the path as the user wrote it, not the resolved one: a
|
|
20234
|
+
* program printing its own usage should echo what was typed. */
|
|
20235
|
+
argv: [module, ...rest],
|
|
20236
|
+
...Object.keys(preopens).length > 0 ? { preopens } : {}
|
|
20237
|
+
});
|
|
20238
|
+
}
|
|
20239
|
+
});
|
|
20240
|
+
function wasiCommands() {
|
|
20241
|
+
return [wasi];
|
|
20242
|
+
}
|
|
19145
20243
|
var platformBuffer = globalThis.Buffer;
|
|
19146
20244
|
var Buffer2 = platformBuffer ?? addBase64UrlSupport(index_js.Buffer);
|
|
19147
20245
|
function addBase64UrlSupport(BufferClass) {
|
|
@@ -19332,8 +20430,8 @@ function verifyIntegrity(data, integrity, shasum, identity) {
|
|
|
19332
20430
|
const dash = choice.indexOf("-");
|
|
19333
20431
|
const algorithm = choice.slice(0, dash);
|
|
19334
20432
|
const expected = choice.slice(dash + 1).replace(/\?.*$/, "");
|
|
19335
|
-
const
|
|
19336
|
-
if (
|
|
20433
|
+
const digest2 = algorithm === "sha512" ? sha512.sha512(data) : algorithm === "sha256" ? sha256.sha256(data) : null;
|
|
20434
|
+
if (digest2 && Buffer2.from(digest2).toString("base64") === expected) return;
|
|
19337
20435
|
}
|
|
19338
20436
|
throw new Error(`Integrity check failed for ${identity}`);
|
|
19339
20437
|
}
|
|
@@ -20303,6 +21401,7 @@ function allCommands() {
|
|
|
20303
21401
|
...nodeCommands(),
|
|
20304
21402
|
...pythonCommands(),
|
|
20305
21403
|
...ffmpegCommands(),
|
|
21404
|
+
...wasiCommands(),
|
|
20306
21405
|
...packageCommands()
|
|
20307
21406
|
];
|
|
20308
21407
|
}
|
|
@@ -20462,15 +21561,15 @@ var Session = class {
|
|
|
20462
21561
|
async run(command, opts = {}) {
|
|
20463
21562
|
if (this.closed) throw new Error("session is closed");
|
|
20464
21563
|
const combined = [];
|
|
20465
|
-
const
|
|
21564
|
+
const decoder9 = new TextDecoder();
|
|
20466
21565
|
const stdout = new BufferSink((chunk) => {
|
|
20467
|
-
const text2 =
|
|
21566
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
20468
21567
|
combined.push(text2);
|
|
20469
21568
|
opts.onStdout?.(text2);
|
|
20470
21569
|
this.hooks.onStdout?.(text2);
|
|
20471
21570
|
});
|
|
20472
21571
|
const stderr = new BufferSink((chunk) => {
|
|
20473
|
-
const text2 =
|
|
21572
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
20474
21573
|
combined.push(text2);
|
|
20475
21574
|
opts.onStderr?.(text2);
|
|
20476
21575
|
this.hooks.onStderr?.(text2);
|
|
@@ -22924,8 +24023,8 @@ function createCryptoModule() {
|
|
|
22924
24023
|
queueMicrotask(() => done?.(null, target));
|
|
22925
24024
|
},
|
|
22926
24025
|
hash(algorithm, data, encoding = "hex") {
|
|
22927
|
-
const
|
|
22928
|
-
return encoding === "buffer" ?
|
|
24026
|
+
const digest2 = Buffer2.from(hashFor(algorithm)(toBytes2(data)));
|
|
24027
|
+
return encoding === "buffer" ? digest2 : digest2.toString(encoding);
|
|
22929
24028
|
},
|
|
22930
24029
|
timingSafeEqual(a, b) {
|
|
22931
24030
|
if (a.length !== b.length) throw new RangeError("Input buffers must have the same byte length");
|
|
@@ -23927,6 +25026,7 @@ function createCoreModules(options) {
|
|
|
23927
25026
|
zlib: zlib_module_default
|
|
23928
25027
|
};
|
|
23929
25028
|
for (const name of stubNames) builtins[name] = createUnsupportedModule(name);
|
|
25029
|
+
builtins.net = createNetModule();
|
|
23930
25030
|
const globals = {
|
|
23931
25031
|
Buffer: Buffer2,
|
|
23932
25032
|
console: consoleObject,
|
|
@@ -24755,15 +25855,43 @@ function createStreamPromises() {
|
|
|
24755
25855
|
})
|
|
24756
25856
|
};
|
|
24757
25857
|
}
|
|
24758
|
-
function createUnsupportedModule(name) {
|
|
25858
|
+
function createUnsupportedModule(name, supported = {}) {
|
|
24759
25859
|
const unsupported = () => {
|
|
24760
25860
|
const error = new Error(`${name} is unavailable in the browser runtime`);
|
|
24761
25861
|
error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
24762
25862
|
throw error;
|
|
24763
25863
|
};
|
|
24764
|
-
|
|
24765
|
-
|
|
24766
|
-
|
|
25864
|
+
const target = { unsupported, ...supported };
|
|
25865
|
+
const module = new Proxy(target, {
|
|
25866
|
+
get(object, key) {
|
|
25867
|
+
if (key === "default") return module;
|
|
25868
|
+
return key in object ? object[key] : unsupported;
|
|
25869
|
+
}
|
|
25870
|
+
});
|
|
25871
|
+
return module;
|
|
25872
|
+
}
|
|
25873
|
+
function createNetModule() {
|
|
25874
|
+
const isIPv4 = (value) => {
|
|
25875
|
+
const parts = String(value).split(".");
|
|
25876
|
+
return parts.length === 4 && parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255 && String(Number(part)) === part);
|
|
25877
|
+
};
|
|
25878
|
+
const isIPv6 = (value) => {
|
|
25879
|
+
const text2 = String(value);
|
|
25880
|
+
if (!text2.includes(":")) return false;
|
|
25881
|
+
const [head2, tail2] = [text2.slice(0, text2.lastIndexOf(":")), text2.slice(text2.lastIndexOf(":") + 1)];
|
|
25882
|
+
const normalised = isIPv4(tail2) ? `${head2}:0:0` : text2;
|
|
25883
|
+
const halves = normalised.split("::");
|
|
25884
|
+
if (halves.length > 2) return false;
|
|
25885
|
+
const groups = halves.map((half) => half === "" ? [] : half.split(":"));
|
|
25886
|
+
if (groups.some((half) => half.some((group) => !/^[0-9a-fA-F]{1,4}$/.test(group)))) return false;
|
|
25887
|
+
const count = groups.reduce((total, half) => total + half.length, 0);
|
|
25888
|
+
return halves.length === 2 ? count <= 7 : count === 8;
|
|
25889
|
+
};
|
|
25890
|
+
return createUnsupportedModule("net", {
|
|
25891
|
+
isIPv4,
|
|
25892
|
+
isIPv6,
|
|
25893
|
+
isIP: (value) => isIPv4(value) ? 4 : isIPv6(value) ? 6 : 0
|
|
25894
|
+
});
|
|
24767
25895
|
}
|
|
24768
25896
|
var stubNames = ["cluster", "dgram", "diagnostics_channel", "domain", "http2", "inspector", "net", "tls", "v8", "vm", "worker_threads"];
|
|
24769
25897
|
var builtinNames2 = [
|
|
@@ -24860,7 +25988,7 @@ var MemoryStat = class {
|
|
|
24860
25988
|
return this.inode.kind === "symlink";
|
|
24861
25989
|
}
|
|
24862
25990
|
};
|
|
24863
|
-
var
|
|
25991
|
+
var encoder7 = new TextEncoder();
|
|
24864
25992
|
var MemoryVolume = class {
|
|
24865
25993
|
entries = /* @__PURE__ */ new Map();
|
|
24866
25994
|
nextIno = 2;
|
|
@@ -24878,7 +26006,7 @@ var MemoryVolume = class {
|
|
|
24878
26006
|
writeFileSync(path, data) {
|
|
24879
26007
|
const key = this.key(path);
|
|
24880
26008
|
this.requireParent(key, "open");
|
|
24881
|
-
const bytes2 = typeof data === "string" ?
|
|
26009
|
+
const bytes2 = typeof data === "string" ? encoder7.encode(data) : data.slice();
|
|
24882
26010
|
const current = this.entries.get(key);
|
|
24883
26011
|
if (current?.kind === "directory") throw new VolumeError("EISDIR", "open", key);
|
|
24884
26012
|
if (current?.kind === "symlink") throw new VolumeError("EINVAL", "open", key);
|
|
@@ -24891,7 +26019,7 @@ var MemoryVolume = class {
|
|
|
24891
26019
|
}
|
|
24892
26020
|
appendFileSync(path, data) {
|
|
24893
26021
|
const key = this.key(path);
|
|
24894
|
-
const bytes2 = typeof data === "string" ?
|
|
26022
|
+
const bytes2 = typeof data === "string" ? encoder7.encode(data) : data;
|
|
24895
26023
|
const current = this.entries.get(key);
|
|
24896
26024
|
if (!current) return this.writeFileSync(key, bytes2);
|
|
24897
26025
|
if (current.kind !== "file") throw new VolumeError(current.kind === "directory" ? "EISDIR" : "EINVAL", "open", key);
|
|
@@ -25029,7 +26157,7 @@ var MemoryVolume = class {
|
|
|
25029
26157
|
if (node2.kind === "directory") directoryCount++;
|
|
25030
26158
|
else {
|
|
25031
26159
|
fileCount++;
|
|
25032
|
-
totalBytes += node2.kind === "file" ? node2.data.length :
|
|
26160
|
+
totalBytes += node2.kind === "file" ? node2.data.length : encoder7.encode(node2.target).length;
|
|
25033
26161
|
}
|
|
25034
26162
|
}
|
|
25035
26163
|
return { totalBytes, fileCount, directoryCount, dirCount: directoryCount };
|
|
@@ -25172,6 +26300,77 @@ var MirroringVolume = class {
|
|
|
25172
26300
|
};
|
|
25173
26301
|
copy(this.root);
|
|
25174
26302
|
}
|
|
26303
|
+
/**
|
|
26304
|
+
* Bring back what the mirrored engine wrote.
|
|
26305
|
+
*
|
|
26306
|
+
* The mirror exists because Rolldown's resolver reads through WASI rather
|
|
26307
|
+
* than through anything JavaScript can hand it — but Rolldown also *writes*
|
|
26308
|
+
* through WASI, so `vite build` leaves its output in the mirror and the
|
|
26309
|
+
* container sees an empty `dist/`. This is the return leg, and it is why the
|
|
26310
|
+
* mirror is no longer strictly one-way.
|
|
26311
|
+
*
|
|
26312
|
+
* Called when a process ends rather than on a timer: that is the moment a
|
|
26313
|
+
* build's output is complete, and a dev server — which serves from memory
|
|
26314
|
+
* and writes nothing — pays for it only once, at exit.
|
|
26315
|
+
*/
|
|
26316
|
+
absorb() {
|
|
26317
|
+
const mirror = this.mirror;
|
|
26318
|
+
if (!mirror?.readdirSync || !mirror.readFileSync || !mirror.lstatSync) return;
|
|
26319
|
+
const visit = (path) => {
|
|
26320
|
+
if (basename(path) === "node_modules") return;
|
|
26321
|
+
let stat2;
|
|
26322
|
+
try {
|
|
26323
|
+
stat2 = mirror.lstatSync(path);
|
|
26324
|
+
} catch {
|
|
26325
|
+
return;
|
|
26326
|
+
}
|
|
26327
|
+
if (stat2.isSymbolicLink()) return;
|
|
26328
|
+
if (stat2.isDirectory()) {
|
|
26329
|
+
let names;
|
|
26330
|
+
try {
|
|
26331
|
+
names = mirror.readdirSync(path);
|
|
26332
|
+
} catch {
|
|
26333
|
+
return;
|
|
26334
|
+
}
|
|
26335
|
+
for (const name of names) visit(join(path, name));
|
|
26336
|
+
return;
|
|
26337
|
+
}
|
|
26338
|
+
let produced;
|
|
26339
|
+
try {
|
|
26340
|
+
produced = mirror.readFileSync(path);
|
|
26341
|
+
} catch {
|
|
26342
|
+
return;
|
|
26343
|
+
}
|
|
26344
|
+
try {
|
|
26345
|
+
const current = this.inner.readFileSync(path);
|
|
26346
|
+
if (sameBytes(current, produced)) return;
|
|
26347
|
+
} catch {
|
|
26348
|
+
}
|
|
26349
|
+
try {
|
|
26350
|
+
this.ensureDirectory(dirname(path));
|
|
26351
|
+
this.inner.writeFileSync(path, produced);
|
|
26352
|
+
} catch {
|
|
26353
|
+
}
|
|
26354
|
+
};
|
|
26355
|
+
visit(this.root);
|
|
26356
|
+
}
|
|
26357
|
+
/** `mkdir -p`, which the volume does not offer directly. */
|
|
26358
|
+
ensureDirectory(path) {
|
|
26359
|
+
const parts = clean(path).split("/").filter(Boolean);
|
|
26360
|
+
let current = "";
|
|
26361
|
+
for (const part of parts) {
|
|
26362
|
+
current += `/${part}`;
|
|
26363
|
+
try {
|
|
26364
|
+
if (this.inner.lstatSync(current).isDirectory()) continue;
|
|
26365
|
+
return;
|
|
26366
|
+
} catch {
|
|
26367
|
+
}
|
|
26368
|
+
try {
|
|
26369
|
+
this.inner.mkdirSync(current);
|
|
26370
|
+
} catch {
|
|
26371
|
+
}
|
|
26372
|
+
}
|
|
26373
|
+
}
|
|
25175
26374
|
/** Is this path inside the mirrored subtree? */
|
|
25176
26375
|
mirrored(path) {
|
|
25177
26376
|
if (!this.mirror) return false;
|
|
@@ -25303,6 +26502,11 @@ var MirroringVolume = class {
|
|
|
25303
26502
|
this.seed();
|
|
25304
26503
|
}
|
|
25305
26504
|
};
|
|
26505
|
+
function sameBytes(a, b) {
|
|
26506
|
+
if (a.length !== b.length) return false;
|
|
26507
|
+
for (let index = 0; index < a.length; index++) if (a[index] !== b[index]) return false;
|
|
26508
|
+
return true;
|
|
26509
|
+
}
|
|
25306
26510
|
|
|
25307
26511
|
// src/runtime/host-esbuild.ts
|
|
25308
26512
|
var ASYNC_API = ["transform", "build", "context", "formatMessages", "analyzeMetafile"];
|
|
@@ -25371,12 +26575,142 @@ function ensureProcessGlobal() {
|
|
|
25371
26575
|
configurable: true
|
|
25372
26576
|
});
|
|
25373
26577
|
}
|
|
26578
|
+
var BINDING_PACKAGE = "@rolldown/binding-wasm32-wasi";
|
|
26579
|
+
var ASYNC_WORK_POOL_SIZE = 4;
|
|
26580
|
+
var WORKER_FILE = "rolldown-wasi-worker.js";
|
|
26581
|
+
async function loadNodeRolldownMemfsBinding() {
|
|
26582
|
+
const require2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
26583
|
+
let packageDir;
|
|
26584
|
+
try {
|
|
26585
|
+
packageDir = path.dirname(require2.resolve(`${BINDING_PACKAGE}/package.json`));
|
|
26586
|
+
} catch {
|
|
26587
|
+
return null;
|
|
26588
|
+
}
|
|
26589
|
+
const fromBinding = module$1.createRequire(path.join(packageDir, "package.json"));
|
|
26590
|
+
let runtime;
|
|
26591
|
+
let fsModuleUrl;
|
|
26592
|
+
let createContext2;
|
|
26593
|
+
try {
|
|
26594
|
+
runtime = fromBinding("@napi-rs/wasm-runtime");
|
|
26595
|
+
fsModuleUrl = resolveSubpath(fromBinding, "@napi-rs/wasm-runtime", "./fs");
|
|
26596
|
+
({ createContext: createContext2 } = fromBinding("@emnapi/runtime"));
|
|
26597
|
+
} catch {
|
|
26598
|
+
return null;
|
|
26599
|
+
}
|
|
26600
|
+
const { memfs } = await import(fsModuleUrl);
|
|
26601
|
+
const { Worker: Worker2 } = await import('worker_threads');
|
|
26602
|
+
const { fs: memoryFs, vol } = memfs();
|
|
26603
|
+
const wasi2 = new runtime.WASI({
|
|
26604
|
+
version: "preview1",
|
|
26605
|
+
fs: memoryFs,
|
|
26606
|
+
preopens: { "/": "/" }
|
|
26607
|
+
});
|
|
26608
|
+
const wasmFile = fs.readFileSync(path.join(packageDir, "rolldown-binding.wasm32-wasi.wasm"));
|
|
26609
|
+
const sharedMemory = new WebAssembly.Memory({ initial: 16384, maximum: 65536, shared: true });
|
|
26610
|
+
const workerPoolSize = Math.max(2, cpuCount());
|
|
26611
|
+
const context = createContext2({ autoDestroy: false });
|
|
26612
|
+
context.suppressDestroy();
|
|
26613
|
+
const workerPath = resolveWorkerPath();
|
|
26614
|
+
if (!workerPath) return null;
|
|
26615
|
+
const runtimeAnchor = url.pathToFileURL(path.join(packageDir, "package.json")).href;
|
|
26616
|
+
const workers = /* @__PURE__ */ new Set();
|
|
26617
|
+
let started = false;
|
|
26618
|
+
const { napiModule } = await runtime.instantiateNapiModule(wasmFile, {
|
|
26619
|
+
context,
|
|
26620
|
+
asyncWorkPoolSize: ASYNC_WORK_POOL_SIZE,
|
|
26621
|
+
reuseWorker: { size: ASYNC_WORK_POOL_SIZE + workerPoolSize },
|
|
26622
|
+
plugins: [runtime.emnapiAsyncWorkPlugin, runtime.emnapiTSFNPlugin],
|
|
26623
|
+
wasi: wasi2,
|
|
26624
|
+
onCreateWorker() {
|
|
26625
|
+
const worker = new Worker2(workerPath, {
|
|
26626
|
+
env: process.env,
|
|
26627
|
+
workerData: { runtimeAnchor, fsModuleUrl },
|
|
26628
|
+
/*
|
|
26629
|
+
* Deliberately no `execArgv`.
|
|
26630
|
+
*
|
|
26631
|
+
* A worker inherits the parent's flags by default, and this worker is
|
|
26632
|
+
* a plain JavaScript file we ship — it needs none of them. Inheriting
|
|
26633
|
+
* them is actively harmful: under a host started with a module loader
|
|
26634
|
+
* (`tsx`, `ts-node`, `--import`), the worker's module resolution then
|
|
26635
|
+
* needs the main thread, which is at that moment blocked waiting for
|
|
26636
|
+
* this very worker to report itself ready. The result is a deadlock at
|
|
26637
|
+
* startup with no error, and it took a while to find.
|
|
26638
|
+
*/
|
|
26639
|
+
execArgv: []
|
|
26640
|
+
});
|
|
26641
|
+
workers.add(worker);
|
|
26642
|
+
const serveFsRequest = runtime.createOnMessage(memoryFs);
|
|
26643
|
+
worker.on("message", (data) => serveFsRequest({ data }));
|
|
26644
|
+
worker.on("exit", () => workers.delete(worker));
|
|
26645
|
+
if (started) unreferenceWorker(worker);
|
|
26646
|
+
return worker;
|
|
26647
|
+
},
|
|
26648
|
+
overwriteImports(importObject) {
|
|
26649
|
+
importObject.env = {
|
|
26650
|
+
...importObject.env,
|
|
26651
|
+
...importObject.napi,
|
|
26652
|
+
...importObject.emnapi,
|
|
26653
|
+
memory: sharedMemory
|
|
26654
|
+
};
|
|
26655
|
+
return importObject;
|
|
26656
|
+
},
|
|
26657
|
+
beforeInit({ instance }) {
|
|
26658
|
+
for (const name of Object.keys(instance.exports)) {
|
|
26659
|
+
if (name.startsWith("__napi_register__")) instance.exports[name]();
|
|
26660
|
+
}
|
|
26661
|
+
}
|
|
26662
|
+
});
|
|
26663
|
+
started = true;
|
|
26664
|
+
for (const worker of workers) unreferenceWorker(worker);
|
|
26665
|
+
return { ...napiModule.exports, __fs: memoryFs, __volume: vol };
|
|
26666
|
+
}
|
|
26667
|
+
function resolveWorkerPath() {
|
|
26668
|
+
const here = path.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
26669
|
+
const candidates = [
|
|
26670
|
+
path.join(here, WORKER_FILE),
|
|
26671
|
+
/* src/runtime → dist, for a checkout that has been built. */
|
|
26672
|
+
path.join(here, "..", "..", "dist", WORKER_FILE)
|
|
26673
|
+
];
|
|
26674
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) ?? null;
|
|
26675
|
+
}
|
|
26676
|
+
function resolveSubpath(require2, packageName, subpath) {
|
|
26677
|
+
const packageDir = path.dirname(require2.resolve(packageName));
|
|
26678
|
+
const manifest = JSON.parse(fs.readFileSync(path.join(packageDir, "package.json"), "utf8"));
|
|
26679
|
+
const entry = manifest?.exports?.[subpath];
|
|
26680
|
+
const target = typeof entry === "string" ? entry : entry?.import ?? entry?.default;
|
|
26681
|
+
if (typeof target !== "string") {
|
|
26682
|
+
throw new Error(`${packageName} does not export ${subpath}`);
|
|
26683
|
+
}
|
|
26684
|
+
return url.pathToFileURL(path.join(packageDir, target)).href;
|
|
26685
|
+
}
|
|
26686
|
+
function cpuCount() {
|
|
26687
|
+
try {
|
|
26688
|
+
return process.availableParallelism?.() ?? 4;
|
|
26689
|
+
} catch {
|
|
26690
|
+
return 4;
|
|
26691
|
+
}
|
|
26692
|
+
}
|
|
26693
|
+
function unreferenceWorker(worker) {
|
|
26694
|
+
for (const key of Object.getOwnPropertySymbols(worker)) {
|
|
26695
|
+
if (key.toString().includes("kPublicPort") || key.toString().includes("kHandle")) {
|
|
26696
|
+
const handle = worker[key];
|
|
26697
|
+
if (handle && typeof handle === "object") handle.ref = () => {
|
|
26698
|
+
};
|
|
26699
|
+
}
|
|
26700
|
+
}
|
|
26701
|
+
worker.unref();
|
|
26702
|
+
}
|
|
25374
26703
|
|
|
25375
26704
|
// src/runtime/host-rolldown.ts
|
|
25376
26705
|
var BUNDLER_HINT = "If this is a bundler pre-bundling the worker away, exclude the binding from dependency optimisation \u2014 in Vite:\n\n optimizeDeps: { exclude: ['@rolldown/binding-wasm32-wasi'] }";
|
|
26706
|
+
var isNodeHost = typeof process !== "undefined" && Boolean(process.versions?.node);
|
|
25377
26707
|
async function loadHostRolldownBinding() {
|
|
25378
26708
|
const reason = syncChannelUnavailableReason();
|
|
25379
26709
|
if (reason) throw new Error(`SandboxedJs threaded WASM unavailable: ${reason}`);
|
|
26710
|
+
if (isNodeHost) {
|
|
26711
|
+
const binding = await loadNodeRolldownMemfsBinding();
|
|
26712
|
+
if (binding) return binding;
|
|
26713
|
+
}
|
|
25380
26714
|
try {
|
|
25381
26715
|
const loaded = await import('@rolldown/binding-wasm32-wasi');
|
|
25382
26716
|
return "__fs" in loaded ? { ...loaded } : loaded.default ?? loaded;
|
|
@@ -25776,6 +27110,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
25776
27110
|
core.cancelTimers();
|
|
25777
27111
|
untrack();
|
|
25778
27112
|
this.router.closeOwner(owner);
|
|
27113
|
+
this.volume.absorb();
|
|
25779
27114
|
}
|
|
25780
27115
|
});
|
|
25781
27116
|
this.running.add(process2);
|
|
@@ -25911,10 +27246,10 @@ function attachRolldownMirror(binding, volume, root) {
|
|
|
25911
27246
|
}
|
|
25912
27247
|
|
|
25913
27248
|
// src/runtime/remote-volume.ts
|
|
25914
|
-
var
|
|
25915
|
-
var
|
|
27249
|
+
var encoder8 = new TextEncoder();
|
|
27250
|
+
var decoder8 = new TextDecoder();
|
|
25916
27251
|
function encodeFrame(header, body) {
|
|
25917
|
-
const json =
|
|
27252
|
+
const json = encoder8.encode(JSON.stringify(header));
|
|
25918
27253
|
const frame = new Uint8Array(4 + json.length + (body?.length ?? 0));
|
|
25919
27254
|
new DataView(frame.buffer).setUint32(0, json.length, true);
|
|
25920
27255
|
frame.set(json, 4);
|
|
@@ -25924,7 +27259,7 @@ function encodeFrame(header, body) {
|
|
|
25924
27259
|
function decodeFrame(frame) {
|
|
25925
27260
|
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
|
25926
27261
|
const headerLength = view.getUint32(0, true);
|
|
25927
|
-
const header = JSON.parse(
|
|
27262
|
+
const header = JSON.parse(decoder8.decode(frame.subarray(4, 4 + headerLength)));
|
|
25928
27263
|
return { header, body: frame.subarray(4 + headerLength) };
|
|
25929
27264
|
}
|
|
25930
27265
|
function flattenStat(stat2) {
|
|
@@ -26727,15 +28062,15 @@ var Container = class _Container {
|
|
|
26727
28062
|
}
|
|
26728
28063
|
makeStdio(opts) {
|
|
26729
28064
|
const combined = [];
|
|
26730
|
-
const
|
|
28065
|
+
const decoder9 = new TextDecoder();
|
|
26731
28066
|
const stdout = new BufferSink((chunk) => {
|
|
26732
|
-
const text2 =
|
|
28067
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
26733
28068
|
combined.push(text2);
|
|
26734
28069
|
opts.onStdout?.(text2);
|
|
26735
28070
|
this.hooks.onStdout?.(text2);
|
|
26736
28071
|
});
|
|
26737
28072
|
const stderr = new BufferSink((chunk) => {
|
|
26738
|
-
const text2 =
|
|
28073
|
+
const text2 = decoder9.decode(chunk, { stream: true });
|
|
26739
28074
|
combined.push(text2);
|
|
26740
28075
|
opts.onStderr?.(text2);
|
|
26741
28076
|
this.hooks.onStderr?.(text2);
|
|
@@ -27480,6 +28815,9 @@ exports.VirtualHttpServer = VirtualHttpServer;
|
|
|
27480
28815
|
exports.VirtualIncomingMessage = VirtualIncomingMessage;
|
|
27481
28816
|
exports.VirtualServerResponse = VirtualServerResponse;
|
|
27482
28817
|
exports.WASM_ALIASES = WASM_ALIASES;
|
|
28818
|
+
exports.WasiError = WasiError;
|
|
28819
|
+
exports.WasiExit = WasiExit;
|
|
28820
|
+
exports.WasiHost = WasiHost;
|
|
27483
28821
|
exports.WorkerRuntimePod = WorkerRuntimePod;
|
|
27484
28822
|
exports.allCommands = allCommands;
|
|
27485
28823
|
exports.applyChmod = applyChmod;
|
|
@@ -27513,6 +28851,7 @@ exports.isBuiltinName = isBuiltinName;
|
|
|
27513
28851
|
exports.isCPythonAvailable = isCPythonAvailable;
|
|
27514
28852
|
exports.isPythonAvailable = isPythonAvailable;
|
|
27515
28853
|
exports.isSysError = isSysError;
|
|
28854
|
+
exports.isWasmBinary = isWasmBinary;
|
|
27516
28855
|
exports.looksLikeEsm = looksLikeEsm;
|
|
27517
28856
|
exports.makeCred = makeCred;
|
|
27518
28857
|
exports.normalizeSignal = normalizeSignal;
|
|
@@ -27522,11 +28861,13 @@ exports.parseUmask = parseUmask;
|
|
|
27522
28861
|
exports.posixPath = path_exports;
|
|
27523
28862
|
exports.renderInto = renderInto;
|
|
27524
28863
|
exports.resetPidCounter = resetPidCounter;
|
|
28864
|
+
exports.runWasi = runWasi;
|
|
27525
28865
|
exports.shellQuote = shellQuote;
|
|
27526
28866
|
exports.startRuntimeWorker = startRuntimeWorker;
|
|
27527
28867
|
exports.strerror = strerror;
|
|
27528
28868
|
exports.syncChannelSupported = syncChannelSupported;
|
|
27529
28869
|
exports.transformEsm = transformEsm;
|
|
27530
28870
|
exports.unameInfo = unameInfo;
|
|
28871
|
+
exports.wasiCommand = wasi;
|
|
27531
28872
|
//# sourceMappingURL=index.cjs.map
|
|
27532
28873
|
//# sourceMappingURL=index.cjs.map
|