sandboxedjs 0.1.5 → 0.1.7
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 +24 -0
- package/dist/index.cjs +39 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +39 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -607,6 +607,30 @@ Those three use dynamic imports, so they only fail if you call them.
|
|
|
607
607
|
removed and the seam is there, but treat browser support as "should work, unproven" rather than
|
|
608
608
|
a tested claim.
|
|
609
609
|
|
|
610
|
+
## Uploading files
|
|
611
|
+
|
|
612
|
+
A file picker in a browser never hands over a path — it hands over the file's bytes. So getting a
|
|
613
|
+
picked file into the container is an ordinary write, and there is nothing to mirror from the host
|
|
614
|
+
disk.
|
|
615
|
+
|
|
616
|
+
Two ways in, both byte-exact:
|
|
617
|
+
|
|
618
|
+
```js
|
|
619
|
+
// 1. Directly, when your own server received the upload.
|
|
620
|
+
await box.fs.writeFile("/workspace/uploads/clip.mp4", bytes); // creates missing directories
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
```js
|
|
624
|
+
// 2. Through a server running inside the container — a picker in the previewed
|
|
625
|
+
// app posting to its own backend. Publish the port and upload normally.
|
|
626
|
+
const bridge = await box.expose(3000);
|
|
627
|
+
await fetch(`${bridge.url}/api/upload`, { method: "POST", body: file });
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
Either way the file lands on the container's filesystem, and `ffmpeg`, Node and Python all see the
|
|
631
|
+
same bytes. Uploads are held in memory like the rest of the filesystem, so a very large video is
|
|
632
|
+
bounded by RAM.
|
|
633
|
+
|
|
610
634
|
## Video and audio
|
|
611
635
|
|
|
612
636
|
`ffmpeg` and `ffprobe` are FFmpeg 5.1 compiled to WebAssembly, mounted on the container's own
|
package/dist/index.cjs
CHANGED
|
@@ -23981,14 +23981,17 @@ var Container = class _Container {
|
|
|
23981
23981
|
/** Send an HTTP request to a server running inside the container. */
|
|
23982
23982
|
async request(port, init = {}) {
|
|
23983
23983
|
this.assertActive();
|
|
23984
|
-
const res = await this.pod.request(port, {
|
|
23984
|
+
const res = await this.dispatchBinary(port, init) ?? await this.pod.request(port, {
|
|
23985
23985
|
method: init.method ?? "GET",
|
|
23986
23986
|
path: init.path ?? "/",
|
|
23987
23987
|
headers: init.headers ?? {},
|
|
23988
23988
|
body: init.body ?? null
|
|
23989
23989
|
});
|
|
23990
23990
|
const bodyRaw = res.body;
|
|
23991
|
-
const bytes = bodyRaw instanceof Uint8Array ? bodyRaw :
|
|
23991
|
+
const bytes = bodyRaw instanceof Uint8Array ? bodyRaw : (
|
|
23992
|
+
// The binary dispatcher answers with an ArrayBuffer.
|
|
23993
|
+
bodyRaw instanceof ArrayBuffer ? new Uint8Array(bodyRaw) : typeof bodyRaw === "string" ? new TextEncoder().encode(bodyRaw) : new Uint8Array(0)
|
|
23994
|
+
);
|
|
23992
23995
|
const body = typeof bodyRaw === "string" ? bodyRaw : new TextDecoder().decode(bytes);
|
|
23993
23996
|
return {
|
|
23994
23997
|
status: res.statusCode ?? 200,
|
|
@@ -24001,6 +24004,40 @@ var Container = class _Container {
|
|
|
24001
24004
|
}
|
|
24002
24005
|
};
|
|
24003
24006
|
}
|
|
24007
|
+
/**
|
|
24008
|
+
* Deliver a request whose body is bytes, without letting them become text.
|
|
24009
|
+
*
|
|
24010
|
+
* Nodepod's public `request()` runs the body through `toString("utf8")` on
|
|
24011
|
+
* its way in, so anything above `0x7f` is replaced: a five-byte payload
|
|
24012
|
+
* containing `0x89` and `0xff` arrives as nine. That silently destroys every
|
|
24013
|
+
* upload — an image or a video reaches the server the wrong size and no
|
|
24014
|
+
* longer decodes, with no error raised anywhere.
|
|
24015
|
+
*
|
|
24016
|
+
* Its own dispatcher one layer down does preserve bytes, so a binary body
|
|
24017
|
+
* goes straight there. This reaches past the published surface deliberately,
|
|
24018
|
+
* so it is written to fail soft: any shape it does not recognise returns
|
|
24019
|
+
* `null` and the caller falls back to the ordinary path, which is exactly
|
|
24020
|
+
* the behaviour that existed before. Text bodies never come through here.
|
|
24021
|
+
*/
|
|
24022
|
+
async dispatchBinary(port, init) {
|
|
24023
|
+
if (!(init.body instanceof Uint8Array)) return null;
|
|
24024
|
+
const manager = this.pod._processManager;
|
|
24025
|
+
const dispatch = manager?.dispatchHttpRequest;
|
|
24026
|
+
if (typeof dispatch !== "function") return null;
|
|
24027
|
+
try {
|
|
24028
|
+
const response = await dispatch.call(
|
|
24029
|
+
manager,
|
|
24030
|
+
port,
|
|
24031
|
+
init.method ?? "GET",
|
|
24032
|
+
init.path ?? "/",
|
|
24033
|
+
init.headers ?? {},
|
|
24034
|
+
init.body
|
|
24035
|
+
);
|
|
24036
|
+
return response ?? null;
|
|
24037
|
+
} catch {
|
|
24038
|
+
return null;
|
|
24039
|
+
}
|
|
24040
|
+
}
|
|
24004
24041
|
/** Wait until something inside the container answers on `port`. */
|
|
24005
24042
|
waitForPort(port, opts = {}) {
|
|
24006
24043
|
return this.net.waitForPort(port, opts);
|