sandboxedjs 0.1.35 → 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/bin/sandboxedjs.mjs +2 -2
- package/dist/index.cjs +249 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +249 -9
- 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/bin/sandboxedjs.mjs
CHANGED
|
@@ -203,8 +203,8 @@ async function printReplBanner(box, opts) {
|
|
|
203
203
|
" npm create vite@latest app -- --template react && cd app && npm i && npm run dev",
|
|
204
204
|
" ffmpeg -f lavfi -i testsrc=size=64x64:rate=5:duration=1 out.mp4 && ls -l out.mp4",
|
|
205
205
|
"",
|
|
206
|
-
`${DIM}Known limits: no
|
|
207
|
-
`
|
|
206
|
+
`${DIM}Known limits: no raw sockets, and esbuild-based toolchains (Vite 4 and`,
|
|
207
|
+
`its contemporaries) cannot start. Current Vite works, build and dev server.${RESET}`,
|
|
208
208
|
"",
|
|
209
209
|
`${DIM}Ctrl-D or \`exit\` to leave.${RESET}`,
|
|
210
210
|
"",
|
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 }; }
|
|
@@ -18677,9 +18681,9 @@ async function resolveIndexUrl() {
|
|
|
18677
18681
|
if (indexUrl) return indexUrl;
|
|
18678
18682
|
if (!isNode2) return DEFAULT_BROWSER_INDEX_URL;
|
|
18679
18683
|
try {
|
|
18680
|
-
const { createRequire } = await nodeBuiltin("module");
|
|
18684
|
+
const { createRequire: createRequire2 } = await nodeBuiltin("module");
|
|
18681
18685
|
const path = await nodeBuiltin("path");
|
|
18682
|
-
const require2 =
|
|
18686
|
+
const require2 = createRequire2(path.join(process.cwd(), "index.js"));
|
|
18683
18687
|
return `${path.dirname(require2.resolve("pyodide/package.json"))}/`;
|
|
18684
18688
|
} catch {
|
|
18685
18689
|
return void 0;
|
|
@@ -19144,15 +19148,15 @@ function pythonCommands() {
|
|
|
19144
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";
|
|
19145
19149
|
var compiled = null;
|
|
19146
19150
|
async function loadFactory() {
|
|
19147
|
-
const { createRequire } = await nodeBuiltin("module");
|
|
19151
|
+
const { createRequire: createRequire2 } = await nodeBuiltin("module");
|
|
19148
19152
|
const fs = await nodeBuiltin("fs/promises");
|
|
19149
19153
|
const path = await nodeBuiltin("path");
|
|
19150
|
-
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)));
|
|
19151
19155
|
let require2 = packageRequire;
|
|
19152
19156
|
try {
|
|
19153
19157
|
require2.resolve("@ffmpeg/core/wasm");
|
|
19154
19158
|
} catch {
|
|
19155
|
-
require2 =
|
|
19159
|
+
require2 = createRequire2(path.join(process.cwd(), "index.js"));
|
|
19156
19160
|
}
|
|
19157
19161
|
const wasmPath = require2.resolve("@ffmpeg/core/wasm");
|
|
19158
19162
|
const wasm = await fs.readFile(wasmPath);
|
|
@@ -25022,6 +25026,7 @@ function createCoreModules(options) {
|
|
|
25022
25026
|
zlib: zlib_module_default
|
|
25023
25027
|
};
|
|
25024
25028
|
for (const name of stubNames) builtins[name] = createUnsupportedModule(name);
|
|
25029
|
+
builtins.net = createNetModule();
|
|
25025
25030
|
const globals = {
|
|
25026
25031
|
Buffer: Buffer2,
|
|
25027
25032
|
console: consoleObject,
|
|
@@ -25850,15 +25855,43 @@ function createStreamPromises() {
|
|
|
25850
25855
|
})
|
|
25851
25856
|
};
|
|
25852
25857
|
}
|
|
25853
|
-
function createUnsupportedModule(name) {
|
|
25858
|
+
function createUnsupportedModule(name, supported = {}) {
|
|
25854
25859
|
const unsupported = () => {
|
|
25855
25860
|
const error = new Error(`${name} is unavailable in the browser runtime`);
|
|
25856
25861
|
error.code = "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM";
|
|
25857
25862
|
throw error;
|
|
25858
25863
|
};
|
|
25859
|
-
|
|
25860
|
-
|
|
25861
|
-
|
|
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
|
+
});
|
|
25862
25895
|
}
|
|
25863
25896
|
var stubNames = ["cluster", "dgram", "diagnostics_channel", "domain", "http2", "inspector", "net", "tls", "v8", "vm", "worker_threads"];
|
|
25864
25897
|
var builtinNames2 = [
|
|
@@ -26267,6 +26300,77 @@ var MirroringVolume = class {
|
|
|
26267
26300
|
};
|
|
26268
26301
|
copy(this.root);
|
|
26269
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
|
+
}
|
|
26270
26374
|
/** Is this path inside the mirrored subtree? */
|
|
26271
26375
|
mirrored(path) {
|
|
26272
26376
|
if (!this.mirror) return false;
|
|
@@ -26398,6 +26502,11 @@ var MirroringVolume = class {
|
|
|
26398
26502
|
this.seed();
|
|
26399
26503
|
}
|
|
26400
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
|
+
}
|
|
26401
26510
|
|
|
26402
26511
|
// src/runtime/host-esbuild.ts
|
|
26403
26512
|
var ASYNC_API = ["transform", "build", "context", "formatMessages", "analyzeMetafile"];
|
|
@@ -26466,12 +26575,142 @@ function ensureProcessGlobal() {
|
|
|
26466
26575
|
configurable: true
|
|
26467
26576
|
});
|
|
26468
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
|
+
}
|
|
26469
26703
|
|
|
26470
26704
|
// src/runtime/host-rolldown.ts
|
|
26471
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);
|
|
26472
26707
|
async function loadHostRolldownBinding() {
|
|
26473
26708
|
const reason = syncChannelUnavailableReason();
|
|
26474
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
|
+
}
|
|
26475
26714
|
try {
|
|
26476
26715
|
const loaded = await import('@rolldown/binding-wasm32-wasi');
|
|
26477
26716
|
return "__fs" in loaded ? { ...loaded } : loaded.default ?? loaded;
|
|
@@ -26871,6 +27110,7 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
26871
27110
|
core.cancelTimers();
|
|
26872
27111
|
untrack();
|
|
26873
27112
|
this.router.closeOwner(owner);
|
|
27113
|
+
this.volume.absorb();
|
|
26874
27114
|
}
|
|
26875
27115
|
});
|
|
26876
27116
|
this.running.add(process2);
|