sandboxedjs 0.1.44 → 0.1.46
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 +23 -3
- package/dist/agent.d.cts +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/{container-CZn9USBQ.d.cts → container-DyRF-bY0.d.cts} +52 -1
- package/dist/{container-CZn9USBQ.d.ts → container-DyRF-bY0.d.ts} +52 -1
- package/dist/index.cjs +245 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -4
- package/dist/index.d.ts +118 -4
- package/dist/index.js +245 -3
- package/dist/index.js.map +1 -1
- package/dist/service-worker.js +352 -8
- package/dist/service-worker.js.map +1 -1
- package/dist/worker-entry.js +133 -1
- package/dist/worker-entry.js.map +1 -1
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -23472,8 +23472,17 @@ var VirtualIncomingMessage = class extends streamModule4.Readable {
|
|
|
23472
23472
|
httpVersionMajor = 1;
|
|
23473
23473
|
httpVersionMinor = 1;
|
|
23474
23474
|
complete = true;
|
|
23475
|
+
/*
|
|
23476
|
+
* Mutable, and `connection` a getter over it, because an upgraded request
|
|
23477
|
+
* carries a real socket rather than the stub. `emit("upgrade", req, socket)`
|
|
23478
|
+
* hands the same object to both places, and a library that reaches it as
|
|
23479
|
+
* `req.socket` — rather than through the argument — has to find the one it
|
|
23480
|
+
* can actually write to.
|
|
23481
|
+
*/
|
|
23475
23482
|
socket = socketStub();
|
|
23476
|
-
connection
|
|
23483
|
+
get connection() {
|
|
23484
|
+
return this.socket;
|
|
23485
|
+
}
|
|
23477
23486
|
constructor(init) {
|
|
23478
23487
|
super();
|
|
23479
23488
|
this.method = (init.method ?? "GET").toUpperCase();
|
|
@@ -23579,6 +23588,80 @@ var VirtualServerResponse = class extends streamModule4.Writable {
|
|
|
23579
23588
|
return this;
|
|
23580
23589
|
}
|
|
23581
23590
|
};
|
|
23591
|
+
var VirtualSocket = class extends streamModule4.Duplex {
|
|
23592
|
+
remoteAddress = "127.0.0.1";
|
|
23593
|
+
remotePort = 0;
|
|
23594
|
+
localAddress = "127.0.0.1";
|
|
23595
|
+
localPort = 0;
|
|
23596
|
+
encrypted = false;
|
|
23597
|
+
bufferSize = 0;
|
|
23598
|
+
peer;
|
|
23599
|
+
/** Set once the peer is told, so `end()` then `destroy()` reports once. */
|
|
23600
|
+
hungUp = false;
|
|
23601
|
+
constructor(peer) {
|
|
23602
|
+
super({ allowHalfOpen: false });
|
|
23603
|
+
this.peer = peer;
|
|
23604
|
+
}
|
|
23605
|
+
_read() {
|
|
23606
|
+
}
|
|
23607
|
+
_write(chunk, encoding, callback) {
|
|
23608
|
+
const bytes2 = Buffer2.isBuffer(chunk) ? chunk : Buffer2.from(chunk, encoding);
|
|
23609
|
+
try {
|
|
23610
|
+
this.peer?.data(new Uint8Array(bytes2));
|
|
23611
|
+
callback();
|
|
23612
|
+
} catch (error) {
|
|
23613
|
+
callback(error);
|
|
23614
|
+
}
|
|
23615
|
+
}
|
|
23616
|
+
_final(callback) {
|
|
23617
|
+
this.hangUp();
|
|
23618
|
+
callback();
|
|
23619
|
+
}
|
|
23620
|
+
_destroy(error, callback) {
|
|
23621
|
+
this.hangUp();
|
|
23622
|
+
callback(error);
|
|
23623
|
+
}
|
|
23624
|
+
hangUp() {
|
|
23625
|
+
if (this.hungUp) return;
|
|
23626
|
+
this.hungUp = true;
|
|
23627
|
+
try {
|
|
23628
|
+
this.peer?.close();
|
|
23629
|
+
} catch {
|
|
23630
|
+
}
|
|
23631
|
+
this.peer = null;
|
|
23632
|
+
}
|
|
23633
|
+
/** Bytes arriving from the far end. */
|
|
23634
|
+
deliver(bytes2) {
|
|
23635
|
+
if (this.destroyed || this.hungUp) return;
|
|
23636
|
+
this.push(Buffer2.from(bytes2));
|
|
23637
|
+
}
|
|
23638
|
+
/** The far end went away; let the server's stream end cleanly. */
|
|
23639
|
+
peerClosed() {
|
|
23640
|
+
if (this.destroyed) return;
|
|
23641
|
+
this.push(null);
|
|
23642
|
+
}
|
|
23643
|
+
/* The parts of `net.Socket` a WebSocket library reaches for. None of them
|
|
23644
|
+
* mean anything without a kernel, but every one of them is called. */
|
|
23645
|
+
setNoDelay() {
|
|
23646
|
+
return this;
|
|
23647
|
+
}
|
|
23648
|
+
setKeepAlive() {
|
|
23649
|
+
return this;
|
|
23650
|
+
}
|
|
23651
|
+
setTimeout(_milliseconds, callback) {
|
|
23652
|
+
if (callback) this.once("timeout", callback);
|
|
23653
|
+
return this;
|
|
23654
|
+
}
|
|
23655
|
+
destroySoon() {
|
|
23656
|
+
this.end();
|
|
23657
|
+
}
|
|
23658
|
+
ref() {
|
|
23659
|
+
return this;
|
|
23660
|
+
}
|
|
23661
|
+
unref() {
|
|
23662
|
+
return this;
|
|
23663
|
+
}
|
|
23664
|
+
};
|
|
23582
23665
|
var VirtualHttpServer = class extends EventEmitter4 {
|
|
23583
23666
|
constructor(router, owner, listener) {
|
|
23584
23667
|
super();
|
|
@@ -23678,6 +23761,33 @@ var VirtualHttpRouter = class {
|
|
|
23678
23761
|
return { statusCode: 500, statusMessage: "Internal Server Error", headers: {}, body: error instanceof Error ? error.message : String(error) };
|
|
23679
23762
|
}
|
|
23680
23763
|
}
|
|
23764
|
+
/**
|
|
23765
|
+
* Open a connection that leaves HTTP behind — a WebSocket, in practice.
|
|
23766
|
+
*
|
|
23767
|
+
* The counterpart to {@link request}, and the thing whose absence made a dev
|
|
23768
|
+
* server look broken. Every Node WebSocket library is built the same way: it
|
|
23769
|
+
* hands `http.Server` an `upgrade` listener and waits. Nothing here ever
|
|
23770
|
+
* emitted one, so `ws` sat holding a server that could not receive a single
|
|
23771
|
+
* connection, and Vite lost the channel it uses to tell a page to reload —
|
|
23772
|
+
* which is the only way it can recover after re-optimizing dependencies.
|
|
23773
|
+
*
|
|
23774
|
+
* Null means there is nothing to connect to: no server on the port, or a
|
|
23775
|
+
* server that never asked for upgrades. Both are refusals the caller should
|
|
23776
|
+
* report rather than wait out.
|
|
23777
|
+
*/
|
|
23778
|
+
connect(port, init, peer) {
|
|
23779
|
+
const item = this.servers.get(port);
|
|
23780
|
+
if (!item) return null;
|
|
23781
|
+
if (item.server.listenerCount("upgrade") === 0) return null;
|
|
23782
|
+
const request = new VirtualIncomingMessage(init);
|
|
23783
|
+
const socket = new VirtualSocket(peer);
|
|
23784
|
+
request.socket = socket;
|
|
23785
|
+
item.server.emit("upgrade", request, socket, Buffer2.alloc(0));
|
|
23786
|
+
return {
|
|
23787
|
+
send: (bytes2) => socket.deliver(bytes2),
|
|
23788
|
+
close: () => socket.peerClosed()
|
|
23789
|
+
};
|
|
23790
|
+
}
|
|
23681
23791
|
};
|
|
23682
23792
|
var VirtualClientResponse = class extends streamModule4.Readable {
|
|
23683
23793
|
constructor(statusCode, statusMessage, headers, body) {
|
|
@@ -27194,6 +27304,9 @@ var LocalRuntimePod = class _LocalRuntimePod {
|
|
|
27194
27304
|
async request(_port, _init = {}) {
|
|
27195
27305
|
return this.router.request(_port, _init);
|
|
27196
27306
|
}
|
|
27307
|
+
connect(port, init, peer) {
|
|
27308
|
+
return this.router.connect(port, init, peer);
|
|
27309
|
+
}
|
|
27197
27310
|
snapshot() {
|
|
27198
27311
|
this.assertActive();
|
|
27199
27312
|
return this.volume.snapshot();
|
|
@@ -27629,6 +27742,15 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27629
27742
|
case "http-response":
|
|
27630
27743
|
this.settleProxied(Number(message.id), message.response);
|
|
27631
27744
|
return;
|
|
27745
|
+
case "ws-data":
|
|
27746
|
+
this.upgraded.get(Number(message.id))?.write(new Uint8Array(message.data));
|
|
27747
|
+
return;
|
|
27748
|
+
case "ws-close": {
|
|
27749
|
+
const socket = this.upgraded.get(Number(message.id));
|
|
27750
|
+
this.upgraded.delete(Number(message.id));
|
|
27751
|
+
socket?.end();
|
|
27752
|
+
return;
|
|
27753
|
+
}
|
|
27632
27754
|
case "child-start":
|
|
27633
27755
|
this.startChild(worker, children, message, ownedChildren);
|
|
27634
27756
|
return;
|
|
@@ -27717,6 +27839,8 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27717
27839
|
// ── HTTP servers living on another thread ─────────────────────────────────
|
|
27718
27840
|
proxies = /* @__PURE__ */ new Map();
|
|
27719
27841
|
waiting = /* @__PURE__ */ new Map();
|
|
27842
|
+
/** Upgraded connections, by the id the Worker knows them as. */
|
|
27843
|
+
upgraded = /* @__PURE__ */ new Map();
|
|
27720
27844
|
nextRequestId = 1;
|
|
27721
27845
|
/**
|
|
27722
27846
|
* Register a stand-in for a server that is actually running in the Worker.
|
|
@@ -27729,6 +27853,9 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27729
27853
|
const server = new VirtualHttpServer(this.router, owner, (request, response) => {
|
|
27730
27854
|
void this.forward(worker, port, request, response);
|
|
27731
27855
|
});
|
|
27856
|
+
server.on("upgrade", (request, socket) => {
|
|
27857
|
+
this.upgrade(worker, port, request, socket);
|
|
27858
|
+
});
|
|
27732
27859
|
try {
|
|
27733
27860
|
server.listen(port);
|
|
27734
27861
|
this.proxies.set(port, server);
|
|
@@ -27759,6 +27886,33 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27759
27886
|
response.writeHead(Number(result.statusCode ?? 200), String(result.statusMessage ?? ""), result.headers ?? {});
|
|
27760
27887
|
response.end(result.body ?? new Uint8Array());
|
|
27761
27888
|
}
|
|
27889
|
+
/**
|
|
27890
|
+
* Tunnel one upgraded connection to the server that actually holds the port.
|
|
27891
|
+
*
|
|
27892
|
+
* Unlike {@link forward} there is no reply to wait for: both ends write
|
|
27893
|
+
* whenever they have something, until one of them stops. The id is what ties
|
|
27894
|
+
* the two directions together across the Worker boundary.
|
|
27895
|
+
*/
|
|
27896
|
+
upgrade(worker, port, request, socket) {
|
|
27897
|
+
const id = this.nextRequestId++;
|
|
27898
|
+
this.upgraded.set(id, socket);
|
|
27899
|
+
socket.on("data", (chunk) => {
|
|
27900
|
+
const bytes2 = new Uint8Array(chunk);
|
|
27901
|
+
worker.postMessage({ type: "ws-data", id, data: bytes2 });
|
|
27902
|
+
});
|
|
27903
|
+
const drop = () => {
|
|
27904
|
+
if (!this.upgraded.delete(id)) return;
|
|
27905
|
+
worker.postMessage({ type: "ws-close", id });
|
|
27906
|
+
};
|
|
27907
|
+
socket.on("close", drop);
|
|
27908
|
+
socket.on("error", drop);
|
|
27909
|
+
worker.postMessage({
|
|
27910
|
+
type: "ws-open",
|
|
27911
|
+
id,
|
|
27912
|
+
port,
|
|
27913
|
+
init: { method: request.method, path: request.url, headers: request.headers }
|
|
27914
|
+
});
|
|
27915
|
+
}
|
|
27762
27916
|
settleProxied(id, response) {
|
|
27763
27917
|
const resolve2 = this.waiting.get(id);
|
|
27764
27918
|
if (!resolve2) return;
|
|
@@ -27771,6 +27925,10 @@ var WorkerRuntimePod = class _WorkerRuntimePod extends LocalRuntimePod {
|
|
|
27771
27925
|
server.close();
|
|
27772
27926
|
this.proxies.delete(port);
|
|
27773
27927
|
}
|
|
27928
|
+
for (const [id, socket] of [...this.upgraded]) {
|
|
27929
|
+
this.upgraded.delete(id);
|
|
27930
|
+
socket.destroy();
|
|
27931
|
+
}
|
|
27774
27932
|
}
|
|
27775
27933
|
teardown() {
|
|
27776
27934
|
for (const { worker, server } of [...this.live]) {
|
|
@@ -28144,6 +28302,29 @@ var Container = class _Container {
|
|
|
28144
28302
|
}
|
|
28145
28303
|
};
|
|
28146
28304
|
}
|
|
28305
|
+
/**
|
|
28306
|
+
* Open a connection to a server inside the container that upgrades out of
|
|
28307
|
+
* HTTP — in practice, a WebSocket.
|
|
28308
|
+
*
|
|
28309
|
+
* The counterpart to {@link request}, and the thing a dev server needs that
|
|
28310
|
+
* a request cannot provide. Bytes go in with `send`, come back through
|
|
28311
|
+
* `peer.data`, and neither side interprets them: the container runs whatever
|
|
28312
|
+
* WebSocket library the program chose, and the caller is responsible for
|
|
28313
|
+
* speaking the protocol it answers with.
|
|
28314
|
+
*
|
|
28315
|
+
* Null means there is nothing to talk to — no server on the port, or one
|
|
28316
|
+
* that never registered an `upgrade` handler. Both are worth reporting
|
|
28317
|
+
* rather than waiting out, because neither resolves on its own.
|
|
28318
|
+
*/
|
|
28319
|
+
connect(port, init = {}, peer) {
|
|
28320
|
+
this.assertActive();
|
|
28321
|
+
if (!this.pod.connect) return null;
|
|
28322
|
+
return this.pod.connect(
|
|
28323
|
+
port,
|
|
28324
|
+
{ method: init.method ?? "GET", path: init.path ?? "/", headers: init.headers ?? {} },
|
|
28325
|
+
peer
|
|
28326
|
+
);
|
|
28327
|
+
}
|
|
28147
28328
|
/**
|
|
28148
28329
|
* Deliver a request whose body is bytes, without letting them become text.
|
|
28149
28330
|
*
|
|
@@ -28715,6 +28896,12 @@ init_arith();
|
|
|
28715
28896
|
init_expand();
|
|
28716
28897
|
init_builtins();
|
|
28717
28898
|
|
|
28899
|
+
// src/preview/ws-client.ts
|
|
28900
|
+
var WS_OPEN = "sandboxedjs:ws-open";
|
|
28901
|
+
var WS_DATA = "sandboxedjs:ws-data";
|
|
28902
|
+
var WS_CLOSE = "sandboxedjs:ws-close";
|
|
28903
|
+
var WS_ERROR = "sandboxedjs:ws-error";
|
|
28904
|
+
|
|
28718
28905
|
// src/preview/register.ts
|
|
28719
28906
|
function serveContainerOn(port, box) {
|
|
28720
28907
|
port.onmessage = async (event) => {
|
|
@@ -28800,13 +28987,65 @@ async function createPreview(box, options = {}) {
|
|
|
28800
28987
|
channel = new MessageChannel();
|
|
28801
28988
|
serveContainerOn(channel.port1, box);
|
|
28802
28989
|
const target = registration.active ?? worker;
|
|
28803
|
-
target.postMessage({ type: "sandboxedjs:connect" }, [channel.port2]);
|
|
28990
|
+
target.postMessage({ type: "sandboxedjs:connect", injectSockets: options.websocket !== false }, [channel.port2]);
|
|
28804
28991
|
};
|
|
28805
28992
|
const onWorkerMessage = (event) => {
|
|
28806
|
-
|
|
28993
|
+
const type = event.data?.type;
|
|
28994
|
+
if (type === "sandboxedjs:host-needed") connect();
|
|
28995
|
+
if (type === "sandboxedjs:stale") options.onStale?.();
|
|
28807
28996
|
};
|
|
28808
28997
|
navigator.serviceWorker.addEventListener("message", onWorkerMessage);
|
|
28809
28998
|
connect();
|
|
28999
|
+
const sockets = /* @__PURE__ */ new Map();
|
|
29000
|
+
const onFrameMessage = (event) => {
|
|
29001
|
+
if (event.origin !== location.origin || !event.source) return;
|
|
29002
|
+
const data = event.data;
|
|
29003
|
+
if (!data || typeof data !== "object") return;
|
|
29004
|
+
if (data.type !== WS_OPEN && data.type !== WS_DATA && data.type !== WS_CLOSE) return;
|
|
29005
|
+
const source = event.source;
|
|
29006
|
+
const id = Number(data.id);
|
|
29007
|
+
const reply = (message, transfer = []) => source.postMessage(message, location.origin, transfer);
|
|
29008
|
+
if (data.type === WS_OPEN) {
|
|
29009
|
+
let table2 = sockets.get(source);
|
|
29010
|
+
if (!table2) sockets.set(source, table2 = /* @__PURE__ */ new Map());
|
|
29011
|
+
let opened = null;
|
|
29012
|
+
try {
|
|
29013
|
+
opened = box.connect(
|
|
29014
|
+
Number(data.port),
|
|
29015
|
+
{ method: "GET", path: data.path ?? "/", headers: data.headers ?? {} },
|
|
29016
|
+
{
|
|
29017
|
+
data: (bytes2) => {
|
|
29018
|
+
const carrier = bytes2.slice().buffer;
|
|
29019
|
+
reply({ type: WS_DATA, id, data: carrier }, [carrier]);
|
|
29020
|
+
},
|
|
29021
|
+
close: () => {
|
|
29022
|
+
table2.delete(id);
|
|
29023
|
+
reply({ type: WS_CLOSE, id });
|
|
29024
|
+
}
|
|
29025
|
+
}
|
|
29026
|
+
);
|
|
29027
|
+
} catch (error) {
|
|
29028
|
+
opened = null;
|
|
29029
|
+
reply({ type: WS_ERROR, id, message: error instanceof Error ? error.message : String(error) });
|
|
29030
|
+
return;
|
|
29031
|
+
}
|
|
29032
|
+
if (!opened) {
|
|
29033
|
+
reply({ type: WS_ERROR, id, message: `Nothing is accepting WebSocket connections on port ${data.port}.` });
|
|
29034
|
+
return;
|
|
29035
|
+
}
|
|
29036
|
+
table2.set(id, opened);
|
|
29037
|
+
return;
|
|
29038
|
+
}
|
|
29039
|
+
const table = sockets.get(source);
|
|
29040
|
+
const socket = table?.get(id);
|
|
29041
|
+
if (!socket) return;
|
|
29042
|
+
if (data.type === WS_DATA) socket.send(new Uint8Array(data.data));
|
|
29043
|
+
else {
|
|
29044
|
+
socket.close();
|
|
29045
|
+
table.delete(id);
|
|
29046
|
+
}
|
|
29047
|
+
};
|
|
29048
|
+
if (typeof window !== "undefined") window.addEventListener("message", onFrameMessage);
|
|
28810
29049
|
const base2 = registration.scope.replace(/\/$/, "");
|
|
28811
29050
|
const urlFor = (port) => `${base2}/__sbx__/${port}/`;
|
|
28812
29051
|
return {
|
|
@@ -28820,6 +29059,9 @@ async function createPreview(box, options = {}) {
|
|
|
28820
29059
|
},
|
|
28821
29060
|
dispose: async () => {
|
|
28822
29061
|
navigator.serviceWorker.removeEventListener("message", onWorkerMessage);
|
|
29062
|
+
if (typeof window !== "undefined") window.removeEventListener("message", onFrameMessage);
|
|
29063
|
+
for (const table of sockets.values()) for (const socket of table.values()) socket.close();
|
|
29064
|
+
sockets.clear();
|
|
28823
29065
|
channel?.port1.close();
|
|
28824
29066
|
channel = null;
|
|
28825
29067
|
await registration.unregister();
|