sandboxedjs 0.1.13 → 0.1.15
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 +49 -7
- package/dist/index.cjs +53 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -15
- package/dist/index.d.ts +39 -15
- package/dist/index.js +52 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -580,8 +580,10 @@ resolve their implementation at call time (`node:zlib`/`node:crypto` on Node,
|
|
|
580
580
|
builtin in when the module loads and a bundler will not fail on it.
|
|
581
581
|
|
|
582
582
|
**You supply the pod.** The default boot path uses `@scelar/nodepod/headless`, which installs a
|
|
583
|
-
`worker_threads` host.
|
|
584
|
-
|
|
583
|
+
`worker_threads` host. That entry is now loaded lazily, behind a runtime-built specifier, so a
|
|
584
|
+
browser bundle never pulls `node:worker_threads` in through it. In a browser you boot Nodepod's
|
|
585
|
+
browser build yourself — it needs a service worker, per Nodepod's own setup docs — and hand the
|
|
586
|
+
instance over:
|
|
585
587
|
|
|
586
588
|
```ts
|
|
587
589
|
import { Nodepod } from "@scelar/nodepod";
|
|
@@ -594,18 +596,58 @@ await box.exec("ls -la /"); // shell + coreutils
|
|
|
594
596
|
await box.exec("node app.js"); // Node, through Nodepod's browser engine
|
|
595
597
|
```
|
|
596
598
|
|
|
597
|
-
**
|
|
599
|
+
**Verified in a browser.** Booted under Vite with Nodepod's browser build, the following run:
|
|
600
|
+
|
|
601
|
+
```
|
|
602
|
+
$ uname -a Linux sandbox 5.10.0 (sandboxedjs@nodepod) … x86_64 GNU/Linux
|
|
603
|
+
$ ls -la /app the mounted files
|
|
604
|
+
$ wc -l /app/data.txt 3 /app/data.txt
|
|
605
|
+
$ grep beta … beta
|
|
606
|
+
$ node /app/hello.js node runtime says hi
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Shell, coreutils, the virtual filesystem and the Node runtime all work. Boot costs about ten
|
|
610
|
+
seconds, nearly all of it Nodepod starting.
|
|
611
|
+
|
|
612
|
+
**What does not work in a browser:**
|
|
598
613
|
|
|
599
614
|
- `copyIn()` / `copyOut()` — they read and write the host filesystem, which does not exist.
|
|
600
615
|
- `expose()` — it opens a real `node:http` listener. In a browser you use Nodepod's service
|
|
601
616
|
worker and preview iframe to reach an in-container server instead of a host port.
|
|
617
|
+
- `python3` — see below.
|
|
602
618
|
- The `sandboxedjs` CLI, obviously.
|
|
603
619
|
|
|
604
|
-
Those
|
|
620
|
+
Those use dynamic imports, so they only fail if you call them.
|
|
621
|
+
|
|
622
|
+
### Python in a browser
|
|
623
|
+
|
|
624
|
+
MicroPython does not currently start in a browser. Two separate things are involved.
|
|
625
|
+
|
|
626
|
+
The first is ours and is fixed: Emscripten resolves `micropython.wasm` against the loader's own
|
|
627
|
+
directory, which is right in Node and wrong once a bundler has rewritten the loader into a hashed
|
|
628
|
+
chunk — the guess lands on a path the dev server answers with `index.html`, and instantiation
|
|
629
|
+
dies on `expected magic word 00 61 73 6d, found 3c 21 64 6f` (the first bytes of `<!doctype`).
|
|
630
|
+
Pass the real URL and that goes away:
|
|
631
|
+
|
|
632
|
+
```ts
|
|
633
|
+
import wasm from "@micropython/micropython-webassembly-pyscript/micropython.wasm?url";
|
|
634
|
+
|
|
635
|
+
await createContainer({ pod, python: { wasmUrl: wasm } });
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
`?url` is Vite's spelling; every bundler has its own. `configurePython({ wasmUrl })` does the
|
|
639
|
+
same thing outside a container.
|
|
640
|
+
|
|
641
|
+
The second is upstream and is not fixed. With the correct binary loaded, instantiation fails on
|
|
642
|
+
|
|
643
|
+
```
|
|
644
|
+
LinkError: Import #45 "env" "__syscall_poll_nonblocking": function import requires a callable
|
|
645
|
+
```
|
|
605
646
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
a
|
|
647
|
+
The symbol appears nowhere in the shipped `micropython.mjs`, so the published glue and `.wasm`
|
|
648
|
+
disagree about their import table. Node is unaffected — the Python tests pass there — and nothing
|
|
649
|
+
in this package can supply a symbol the loader never declares. Treat `python3` as Node-only until
|
|
650
|
+
the upstream package ships a matching pair.
|
|
609
651
|
|
|
610
652
|
## Uploading files
|
|
611
653
|
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var nodepod = require('@scelar/nodepod');
|
|
6
6
|
var acorn = require('acorn');
|
|
7
7
|
|
|
8
8
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
@@ -8553,6 +8553,15 @@ async function nodeBuiltin(name) {
|
|
|
8553
8553
|
specifier
|
|
8554
8554
|
);
|
|
8555
8555
|
}
|
|
8556
|
+
async function nodeOnlyModule(specifier) {
|
|
8557
|
+
const parts = specifier.split("/");
|
|
8558
|
+
const runtimeSpecifier = parts.join("/");
|
|
8559
|
+
return await import(
|
|
8560
|
+
/* @vite-ignore */
|
|
8561
|
+
/* webpackIgnore: true */
|
|
8562
|
+
runtimeSpecifier
|
|
8563
|
+
);
|
|
8564
|
+
}
|
|
8556
8565
|
var zlibPromise = null;
|
|
8557
8566
|
function nodeZlib() {
|
|
8558
8567
|
zlibPromise ??= nodeBuiltin("zlib");
|
|
@@ -8755,7 +8764,7 @@ function installEsbuildRuntime() {
|
|
|
8755
8764
|
async function install() {
|
|
8756
8765
|
const workerPath = await buildPatchedWorker();
|
|
8757
8766
|
if (!workerPath) return;
|
|
8758
|
-
const { createNodeHost, setRuntimeHost } = await
|
|
8767
|
+
const { createNodeHost, setRuntimeHost } = await nodeOnlyModule("@scelar/nodepod/headless");
|
|
8759
8768
|
setRuntimeHost(createNodeHost({ workerPath }));
|
|
8760
8769
|
}
|
|
8761
8770
|
async function buildPatchedWorker() {
|
|
@@ -22144,6 +22153,10 @@ init_path();
|
|
|
22144
22153
|
var PYTHON_VERSION = "3.4.0";
|
|
22145
22154
|
var MICROPYTHON_BANNER = "MicroPython v1.28.0 on 2026-04-06; SandboxedJS with Emscripten";
|
|
22146
22155
|
var loaderPromise = null;
|
|
22156
|
+
var wasmUrl;
|
|
22157
|
+
function configurePython(options = {}) {
|
|
22158
|
+
if (options.wasmUrl !== void 0) wasmUrl = options.wasmUrl;
|
|
22159
|
+
}
|
|
22147
22160
|
async function getLoader() {
|
|
22148
22161
|
if (!loaderPromise) {
|
|
22149
22162
|
loaderPromise = import('@micropython/micropython-webassembly-pyscript/micropython.mjs').then(
|
|
@@ -22166,6 +22179,7 @@ async function createInterpreter(ctx, opts) {
|
|
|
22166
22179
|
stdout: opts.stdout,
|
|
22167
22180
|
stderr: opts.stderr,
|
|
22168
22181
|
...opts.stdin ? { stdin: opts.stdin } : {},
|
|
22182
|
+
...wasmUrl ? { url: wasmUrl } : {},
|
|
22169
22183
|
linebuffer: false,
|
|
22170
22184
|
heapsize: opts.heapsize ?? 64 * 1024 * 1024
|
|
22171
22185
|
});
|
|
@@ -22547,7 +22561,13 @@ async function loadFactory() {
|
|
|
22547
22561
|
const { createRequire } = await nodeBuiltin("module");
|
|
22548
22562
|
const fs = await nodeBuiltin("fs/promises");
|
|
22549
22563
|
const path = await nodeBuiltin("path");
|
|
22550
|
-
const
|
|
22564
|
+
const packageRequire = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
22565
|
+
let require2 = packageRequire;
|
|
22566
|
+
try {
|
|
22567
|
+
require2.resolve("@ffmpeg/core/wasm");
|
|
22568
|
+
} catch {
|
|
22569
|
+
require2 = createRequire(path.join(process.cwd(), "index.js"));
|
|
22570
|
+
}
|
|
22551
22571
|
const wasmPath = require2.resolve("@ffmpeg/core/wasm");
|
|
22552
22572
|
const wasm = await fs.readFile(wasmPath);
|
|
22553
22573
|
const module = await Promise.resolve().then(() => (init_ffmpeg_core(), ffmpeg_core_exports));
|
|
@@ -22884,7 +22904,7 @@ function splitPackageSpec(spec) {
|
|
|
22884
22904
|
return { name: spec.slice(0, at), version: spec.slice(at + 1) };
|
|
22885
22905
|
}
|
|
22886
22906
|
async function installPackages(ctx, specs, opts) {
|
|
22887
|
-
const installer = opts.cwd === "/" ? ctx.kernel.pod.packages : new
|
|
22907
|
+
const installer = opts.cwd === "/" ? ctx.kernel.pod.packages : new nodepod.DependencyInstaller(ctx.vfs.volume, { cwd: opts.cwd });
|
|
22888
22908
|
const onProgress = (message) => {
|
|
22889
22909
|
if (!opts.quiet) ctx.stderr.write(message.endsWith("\n") ? message : message + "\n");
|
|
22890
22910
|
};
|
|
@@ -23176,6 +23196,12 @@ function packageBinaries(ctx, root, packageName) {
|
|
|
23176
23196
|
}
|
|
23177
23197
|
return [];
|
|
23178
23198
|
}
|
|
23199
|
+
var CLI_MOVED_TO = {
|
|
23200
|
+
tailwindcss: "@tailwindcss/cli",
|
|
23201
|
+
postcss: "postcss-cli",
|
|
23202
|
+
autoprefixer: "postcss-cli",
|
|
23203
|
+
sass: "sass-embedded"
|
|
23204
|
+
};
|
|
23179
23205
|
function findLocalBin(ctx, name) {
|
|
23180
23206
|
let dir3 = ctx.cwd;
|
|
23181
23207
|
for (let i = 0; i < 64; i++) {
|
|
@@ -23282,6 +23308,14 @@ unless the container was created with network: { allowOutbound: true }.`,
|
|
|
23282
23308
|
const chosen = binaries.includes(command) ? command : binaries[0];
|
|
23283
23309
|
if (chosen === void 0) {
|
|
23284
23310
|
ctx.warn(`could not determine executable to run: ${packageName} provides no binary`);
|
|
23311
|
+
const replacement = CLI_MOVED_TO[packageName];
|
|
23312
|
+
if (replacement !== void 0) {
|
|
23313
|
+
ctx.warn(`'${packageName}' ships no CLI; its command lives in '${replacement}'.`);
|
|
23314
|
+
ctx.warn(`Try: npx ${replacement} ${rest.join(" ")}`.trimEnd());
|
|
23315
|
+
} else {
|
|
23316
|
+
ctx.warn(`'${packageName}' is installed but declares no "bin" entry.`);
|
|
23317
|
+
ctx.warn("Check which package provides the command, or use `npx -p <pkg> <command>`.");
|
|
23318
|
+
}
|
|
23285
23319
|
return 127;
|
|
23286
23320
|
}
|
|
23287
23321
|
command = chosen;
|
|
@@ -23858,14 +23892,9 @@ var Container = class _Container {
|
|
|
23858
23892
|
}
|
|
23859
23893
|
// ── boot ──────────────────────────────────────────────────────────────────
|
|
23860
23894
|
static async create(opts = {}) {
|
|
23895
|
+
if (opts.python) configurePython(opts.python);
|
|
23861
23896
|
if (!opts.pod) await installEsbuildRuntime();
|
|
23862
|
-
const pod = opts.pod ?? await
|
|
23863
|
-
headless: true,
|
|
23864
|
-
serviceWorker: false,
|
|
23865
|
-
env: opts.env ?? {},
|
|
23866
|
-
workdir: opts.cwd ?? "/",
|
|
23867
|
-
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
23868
|
-
});
|
|
23897
|
+
const pod = opts.pod ?? await bootHeadlessPod(opts);
|
|
23869
23898
|
const kernel = new Kernel({
|
|
23870
23899
|
pod,
|
|
23871
23900
|
hostname: opts.hostname ?? "sandbox",
|
|
@@ -24287,6 +24316,18 @@ function normalizeHeaders(headers) {
|
|
|
24287
24316
|
async function createContainer(opts = {}) {
|
|
24288
24317
|
return Container.create(opts);
|
|
24289
24318
|
}
|
|
24319
|
+
async function bootHeadlessPod(opts) {
|
|
24320
|
+
const { Nodepod } = await nodeOnlyModule(
|
|
24321
|
+
"@scelar/nodepod/headless"
|
|
24322
|
+
);
|
|
24323
|
+
return Nodepod.boot({
|
|
24324
|
+
headless: true,
|
|
24325
|
+
serviceWorker: false,
|
|
24326
|
+
env: opts.env ?? {},
|
|
24327
|
+
workdir: opts.cwd ?? "/",
|
|
24328
|
+
...opts.onServerReady ? { onServerReady: opts.onServerReady } : {}
|
|
24329
|
+
});
|
|
24330
|
+
}
|
|
24290
24331
|
|
|
24291
24332
|
// src/container/terminal.ts
|
|
24292
24333
|
init_path();
|
|
@@ -24765,6 +24806,7 @@ exports.braceExpand = braceExpand;
|
|
|
24765
24806
|
exports.buildRootfs = buildRootfs;
|
|
24766
24807
|
exports.builtinNames = builtinNames;
|
|
24767
24808
|
exports.captureStdio = captureStdio;
|
|
24809
|
+
exports.configurePython = configurePython;
|
|
24768
24810
|
exports.createContainer = createContainer;
|
|
24769
24811
|
exports.createContext = createContext;
|
|
24770
24812
|
exports.default = src_default;
|