sandboxedjs 0.1.45 → 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 +242 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -4
- package/dist/index.d.ts +104 -4
- package/dist/index.js +242 -2
- package/dist/index.js.map +1 -1
- package/dist/service-worker.js +340 -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.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Shell, a as ShellIO, b as Session, V as Vfs, c as Cred, N as Node, d as Command, K as Kernel, R as RuntimeVolume, e as VolumeStat, f as VolumeStats, g as RuntimeHttpResponse, h as SpawnChild, i as SyncSpawn, j as RuntimePod, k as RuntimePackageInstaller, l as ChildSpawnConfig, m as ChildHandle, n as RuntimeProcess, E as ExecContext, O as OutputStream, C as Container,
|
|
2
|
-
export { B as BufferSink,
|
|
1
|
+
import { S as Shell, a as ShellIO, b as Session, V as Vfs, c as Cred, N as Node, d as Command, K as Kernel, R as RuntimeVolume, e as VolumeStat, f as VolumeStats, g as RuntimeHttpResponse, h as SpawnChild, i as SyncSpawn, j as RuntimePod, k as RuntimePackageInstaller, l as ChildSpawnConfig, m as ChildHandle, n as RuntimeProcess, o as RuntimeSocketPeer, p as RuntimeConnection, E as ExecContext, O as OutputStream, C as Container, q as createContainer } from './container-DyRF-bY0.cjs';
|
|
2
|
+
export { B as BufferSink, r as CPythonOptions, s as CallbackSink, t as CommandRegistry, u as ContainerFs, v as ContainerOptions, w as ContextInit, D as DirEntry, x as Env, y as ExecOptions, z as ExecResult, F as FileData, A as FileInput, G as FileOutput, H as GroupEntry, I as HttpResponse, J as InputStream, L as Job, M as KernelOptions, P as ListeningPort, Q as MountEntry, T as NetInterface, U as NetworkOptions, W as NetworkStack, X as NullInput, Y as NullOutput, Z as PYTHON_VERSION, _ as PasswdEntry, $ as Pipe, a0 as Process, a1 as ProcessKind, a2 as ProcessOptions, a3 as ProcessState, a4 as ProcessTable, a5 as PythonOptions, a6 as ROOT_CRED, a7 as ResolvedExecutable, a8 as RunOptions, a9 as RunResult, aa as RuntimeProcessManager, ab as RuntimeProcessResult, ac as SessionInit, ad as SessionResult, ae as SessionRunOptions, af as ShellExit, ag as ShellInit, ah as ShellOptions, ai as SpawnHandle, aj as Stats, ak as Stdio, al as TeeOutput, am as UserDatabase, an as Variables, ao as VirtualNode, ap as VirtualProvider, aq as WriteOptions, ar as applyChmod, as as braceExpand, at as captureStdio, au as configureCPython, av as configurePython, aw as createChildProcessModule, ax as createContext, ay as defineCommand, az as expandWord, aA as expandWords, aB as formatMode, aC as isCPythonAvailable, aD as isPythonAvailable, aE as makeCred, aF as octalMode, aG as parseUmask, aH as resetPidCounter, aI as shellQuote } from './container-DyRF-bY0.cjs';
|
|
3
3
|
import EventEmitter from 'events/events.js';
|
|
4
4
|
import streamModule from 'stream-browserify';
|
|
5
5
|
|
|
@@ -648,8 +648,8 @@ declare class VirtualIncomingMessage extends streamModule.Readable {
|
|
|
648
648
|
readonly httpVersionMajor = 1;
|
|
649
649
|
readonly httpVersionMinor = 1;
|
|
650
650
|
readonly complete = true;
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
socket: Record<string, unknown> | VirtualSocket;
|
|
652
|
+
get connection(): Record<string, unknown> | VirtualSocket;
|
|
653
653
|
constructor(init: VirtualRequestInit);
|
|
654
654
|
_read(): void;
|
|
655
655
|
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
@@ -683,6 +683,60 @@ declare class VirtualServerResponse extends streamModule.Writable {
|
|
|
683
683
|
addTrailers(_headers: Record<string, string>): void;
|
|
684
684
|
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
685
685
|
}
|
|
686
|
+
/** Where a {@link VirtualSocket} puts the bytes the server writes. */
|
|
687
|
+
interface VirtualSocketPeer {
|
|
688
|
+
/** The server sent these. */
|
|
689
|
+
data(bytes: Uint8Array): void;
|
|
690
|
+
/** The server hung up. */
|
|
691
|
+
close(): void;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* The server half of a connection that has stopped being HTTP.
|
|
695
|
+
*
|
|
696
|
+
* Everything else in this file models one request and one response, because
|
|
697
|
+
* that is all a container's servers were ever asked for. A protocol upgrade is
|
|
698
|
+
* the case that does not fit: after the `101` there is no request and no
|
|
699
|
+
* response, only two peers writing bytes at each other for as long as they
|
|
700
|
+
* both stay interested.
|
|
701
|
+
*
|
|
702
|
+
* So this is a real `Duplex` rather than another stub. It has to be — the
|
|
703
|
+
* libraries that speak WebSocket do not merely read `req.headers` and reply,
|
|
704
|
+
* they take the socket and run a framing protocol over it, and a stub that
|
|
705
|
+
* accepts `write()` and drops it produces a server that completes its
|
|
706
|
+
* handshake and is then silent forever, which looks exactly like a network
|
|
707
|
+
* problem and is the hardest possible thing to attribute.
|
|
708
|
+
*
|
|
709
|
+
* Writes go out through {@link VirtualSocketPeer}; {@link deliver} pushes what
|
|
710
|
+
* comes back. Nothing here knows what the bytes mean, which is the point: `ws`,
|
|
711
|
+
* `socket.io` and Vite's HMR server all work over it unmodified.
|
|
712
|
+
*/
|
|
713
|
+
declare class VirtualSocket extends streamModule.Duplex {
|
|
714
|
+
readonly remoteAddress = "127.0.0.1";
|
|
715
|
+
readonly remotePort = 0;
|
|
716
|
+
readonly localAddress = "127.0.0.1";
|
|
717
|
+
readonly localPort = 0;
|
|
718
|
+
readonly encrypted = false;
|
|
719
|
+
readonly bufferSize = 0;
|
|
720
|
+
private peer;
|
|
721
|
+
/** Set once the peer is told, so `end()` then `destroy()` reports once. */
|
|
722
|
+
private hungUp;
|
|
723
|
+
constructor(peer: VirtualSocketPeer);
|
|
724
|
+
_read(): void;
|
|
725
|
+
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
|
|
726
|
+
_final(callback: (error?: Error | null) => void): void;
|
|
727
|
+
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
|
|
728
|
+
private hangUp;
|
|
729
|
+
/** Bytes arriving from the far end. */
|
|
730
|
+
deliver(bytes: Uint8Array): void;
|
|
731
|
+
/** The far end went away; let the server's stream end cleanly. */
|
|
732
|
+
peerClosed(): void;
|
|
733
|
+
setNoDelay(): this;
|
|
734
|
+
setKeepAlive(): this;
|
|
735
|
+
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
736
|
+
destroySoon(): void;
|
|
737
|
+
ref(): this;
|
|
738
|
+
unref(): this;
|
|
739
|
+
}
|
|
686
740
|
declare class VirtualHttpServer extends EventEmitter {
|
|
687
741
|
private readonly router;
|
|
688
742
|
readonly owner: string;
|
|
@@ -716,6 +770,28 @@ declare class VirtualHttpRouter {
|
|
|
716
770
|
closeOwner(owner: string): void;
|
|
717
771
|
closeAll(): void;
|
|
718
772
|
request(port: number, init?: VirtualRequestInit): Promise<RuntimeHttpResponse>;
|
|
773
|
+
/**
|
|
774
|
+
* Open a connection that leaves HTTP behind — a WebSocket, in practice.
|
|
775
|
+
*
|
|
776
|
+
* The counterpart to {@link request}, and the thing whose absence made a dev
|
|
777
|
+
* server look broken. Every Node WebSocket library is built the same way: it
|
|
778
|
+
* hands `http.Server` an `upgrade` listener and waits. Nothing here ever
|
|
779
|
+
* emitted one, so `ws` sat holding a server that could not receive a single
|
|
780
|
+
* connection, and Vite lost the channel it uses to tell a page to reload —
|
|
781
|
+
* which is the only way it can recover after re-optimizing dependencies.
|
|
782
|
+
*
|
|
783
|
+
* Null means there is nothing to connect to: no server on the port, or a
|
|
784
|
+
* server that never asked for upgrades. Both are refusals the caller should
|
|
785
|
+
* report rather than wait out.
|
|
786
|
+
*/
|
|
787
|
+
connect(port: number, init: VirtualRequestInit, peer: VirtualSocketPeer): VirtualConnection | null;
|
|
788
|
+
}
|
|
789
|
+
/** A connection handed back by {@link VirtualHttpRouter.connect}. */
|
|
790
|
+
interface VirtualConnection {
|
|
791
|
+
/** Bytes from the far end, into the container. */
|
|
792
|
+
send(bytes: Uint8Array): void;
|
|
793
|
+
/** The far end went away. */
|
|
794
|
+
close(): void;
|
|
719
795
|
}
|
|
720
796
|
|
|
721
797
|
interface CoreModulesOptions {
|
|
@@ -1029,6 +1105,7 @@ declare class LocalRuntimePod implements RuntimePod {
|
|
|
1029
1105
|
*/
|
|
1030
1106
|
private settle;
|
|
1031
1107
|
request(_port: number, _init?: Record<string, unknown>): Promise<RuntimeHttpResponse>;
|
|
1108
|
+
connect(port: number, init: Record<string, unknown>, peer: RuntimeSocketPeer): RuntimeConnection | null;
|
|
1032
1109
|
snapshot(): MemoryVolumeSnapshotEntry[];
|
|
1033
1110
|
restore(snapshot: unknown): Promise<void>;
|
|
1034
1111
|
teardown(): void;
|
|
@@ -1076,6 +1153,8 @@ declare class WorkerRuntimePod extends LocalRuntimePod {
|
|
|
1076
1153
|
private runChildToCompletion;
|
|
1077
1154
|
private readonly proxies;
|
|
1078
1155
|
private readonly waiting;
|
|
1156
|
+
/** Upgraded connections, by the id the Worker knows them as. */
|
|
1157
|
+
private readonly upgraded;
|
|
1079
1158
|
private nextRequestId;
|
|
1080
1159
|
/**
|
|
1081
1160
|
* Register a stand-in for a server that is actually running in the Worker.
|
|
@@ -1085,6 +1164,14 @@ declare class WorkerRuntimePod extends LocalRuntimePod {
|
|
|
1085
1164
|
*/
|
|
1086
1165
|
private proxyPort;
|
|
1087
1166
|
private forward;
|
|
1167
|
+
/**
|
|
1168
|
+
* Tunnel one upgraded connection to the server that actually holds the port.
|
|
1169
|
+
*
|
|
1170
|
+
* Unlike {@link forward} there is no reply to wait for: both ends write
|
|
1171
|
+
* whenever they have something, until one of them stops. The id is what ties
|
|
1172
|
+
* the two directions together across the Worker boundary.
|
|
1173
|
+
*/
|
|
1174
|
+
private upgrade;
|
|
1088
1175
|
private settleProxied;
|
|
1089
1176
|
private closeProxies;
|
|
1090
1177
|
teardown(): void;
|
|
@@ -1465,6 +1552,19 @@ interface PreviewOptions {
|
|
|
1465
1552
|
* the moment, and it can stop after a few attempts rather than looping.
|
|
1466
1553
|
*/
|
|
1467
1554
|
onStale?: () => void;
|
|
1555
|
+
/**
|
|
1556
|
+
* Let pages inside the preview open WebSockets to the container.
|
|
1557
|
+
*
|
|
1558
|
+
* On by default. A service worker cannot answer a WebSocket handshake, so
|
|
1559
|
+
* previewed documents are given a `WebSocket` that tunnels through this page
|
|
1560
|
+
* instead — which means the HTML they are served is modified on the way
|
|
1561
|
+
* through, one `<script>` at the top of `<head>`.
|
|
1562
|
+
*
|
|
1563
|
+
* Turn it off to serve documents byte for byte. The cost is that a dev
|
|
1564
|
+
* server loses its HMR channel, and with it the `full-reload` it sends after
|
|
1565
|
+
* re-optimizing dependencies; {@link onStale} is the fallback for that.
|
|
1566
|
+
*/
|
|
1567
|
+
websocket?: boolean;
|
|
1468
1568
|
}
|
|
1469
1569
|
interface Preview {
|
|
1470
1570
|
/** The URL an iframe should be pointed at to see `port`. */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { S as Shell, a as ShellIO, b as Session, V as Vfs, c as Cred, N as Node, d as Command, K as Kernel, R as RuntimeVolume, e as VolumeStat, f as VolumeStats, g as RuntimeHttpResponse, h as SpawnChild, i as SyncSpawn, j as RuntimePod, k as RuntimePackageInstaller, l as ChildSpawnConfig, m as ChildHandle, n as RuntimeProcess, E as ExecContext, O as OutputStream, C as Container,
|
|
2
|
-
export { B as BufferSink,
|
|
1
|
+
import { S as Shell, a as ShellIO, b as Session, V as Vfs, c as Cred, N as Node, d as Command, K as Kernel, R as RuntimeVolume, e as VolumeStat, f as VolumeStats, g as RuntimeHttpResponse, h as SpawnChild, i as SyncSpawn, j as RuntimePod, k as RuntimePackageInstaller, l as ChildSpawnConfig, m as ChildHandle, n as RuntimeProcess, o as RuntimeSocketPeer, p as RuntimeConnection, E as ExecContext, O as OutputStream, C as Container, q as createContainer } from './container-DyRF-bY0.js';
|
|
2
|
+
export { B as BufferSink, r as CPythonOptions, s as CallbackSink, t as CommandRegistry, u as ContainerFs, v as ContainerOptions, w as ContextInit, D as DirEntry, x as Env, y as ExecOptions, z as ExecResult, F as FileData, A as FileInput, G as FileOutput, H as GroupEntry, I as HttpResponse, J as InputStream, L as Job, M as KernelOptions, P as ListeningPort, Q as MountEntry, T as NetInterface, U as NetworkOptions, W as NetworkStack, X as NullInput, Y as NullOutput, Z as PYTHON_VERSION, _ as PasswdEntry, $ as Pipe, a0 as Process, a1 as ProcessKind, a2 as ProcessOptions, a3 as ProcessState, a4 as ProcessTable, a5 as PythonOptions, a6 as ROOT_CRED, a7 as ResolvedExecutable, a8 as RunOptions, a9 as RunResult, aa as RuntimeProcessManager, ab as RuntimeProcessResult, ac as SessionInit, ad as SessionResult, ae as SessionRunOptions, af as ShellExit, ag as ShellInit, ah as ShellOptions, ai as SpawnHandle, aj as Stats, ak as Stdio, al as TeeOutput, am as UserDatabase, an as Variables, ao as VirtualNode, ap as VirtualProvider, aq as WriteOptions, ar as applyChmod, as as braceExpand, at as captureStdio, au as configureCPython, av as configurePython, aw as createChildProcessModule, ax as createContext, ay as defineCommand, az as expandWord, aA as expandWords, aB as formatMode, aC as isCPythonAvailable, aD as isPythonAvailable, aE as makeCred, aF as octalMode, aG as parseUmask, aH as resetPidCounter, aI as shellQuote } from './container-DyRF-bY0.js';
|
|
3
3
|
import EventEmitter from 'events/events.js';
|
|
4
4
|
import streamModule from 'stream-browserify';
|
|
5
5
|
|
|
@@ -648,8 +648,8 @@ declare class VirtualIncomingMessage extends streamModule.Readable {
|
|
|
648
648
|
readonly httpVersionMajor = 1;
|
|
649
649
|
readonly httpVersionMinor = 1;
|
|
650
650
|
readonly complete = true;
|
|
651
|
-
|
|
652
|
-
|
|
651
|
+
socket: Record<string, unknown> | VirtualSocket;
|
|
652
|
+
get connection(): Record<string, unknown> | VirtualSocket;
|
|
653
653
|
constructor(init: VirtualRequestInit);
|
|
654
654
|
_read(): void;
|
|
655
655
|
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
@@ -683,6 +683,60 @@ declare class VirtualServerResponse extends streamModule.Writable {
|
|
|
683
683
|
addTrailers(_headers: Record<string, string>): void;
|
|
684
684
|
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
685
685
|
}
|
|
686
|
+
/** Where a {@link VirtualSocket} puts the bytes the server writes. */
|
|
687
|
+
interface VirtualSocketPeer {
|
|
688
|
+
/** The server sent these. */
|
|
689
|
+
data(bytes: Uint8Array): void;
|
|
690
|
+
/** The server hung up. */
|
|
691
|
+
close(): void;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* The server half of a connection that has stopped being HTTP.
|
|
695
|
+
*
|
|
696
|
+
* Everything else in this file models one request and one response, because
|
|
697
|
+
* that is all a container's servers were ever asked for. A protocol upgrade is
|
|
698
|
+
* the case that does not fit: after the `101` there is no request and no
|
|
699
|
+
* response, only two peers writing bytes at each other for as long as they
|
|
700
|
+
* both stay interested.
|
|
701
|
+
*
|
|
702
|
+
* So this is a real `Duplex` rather than another stub. It has to be — the
|
|
703
|
+
* libraries that speak WebSocket do not merely read `req.headers` and reply,
|
|
704
|
+
* they take the socket and run a framing protocol over it, and a stub that
|
|
705
|
+
* accepts `write()` and drops it produces a server that completes its
|
|
706
|
+
* handshake and is then silent forever, which looks exactly like a network
|
|
707
|
+
* problem and is the hardest possible thing to attribute.
|
|
708
|
+
*
|
|
709
|
+
* Writes go out through {@link VirtualSocketPeer}; {@link deliver} pushes what
|
|
710
|
+
* comes back. Nothing here knows what the bytes mean, which is the point: `ws`,
|
|
711
|
+
* `socket.io` and Vite's HMR server all work over it unmodified.
|
|
712
|
+
*/
|
|
713
|
+
declare class VirtualSocket extends streamModule.Duplex {
|
|
714
|
+
readonly remoteAddress = "127.0.0.1";
|
|
715
|
+
readonly remotePort = 0;
|
|
716
|
+
readonly localAddress = "127.0.0.1";
|
|
717
|
+
readonly localPort = 0;
|
|
718
|
+
readonly encrypted = false;
|
|
719
|
+
readonly bufferSize = 0;
|
|
720
|
+
private peer;
|
|
721
|
+
/** Set once the peer is told, so `end()` then `destroy()` reports once. */
|
|
722
|
+
private hungUp;
|
|
723
|
+
constructor(peer: VirtualSocketPeer);
|
|
724
|
+
_read(): void;
|
|
725
|
+
_write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
|
|
726
|
+
_final(callback: (error?: Error | null) => void): void;
|
|
727
|
+
_destroy(error: Error | null, callback: (error?: Error | null) => void): void;
|
|
728
|
+
private hangUp;
|
|
729
|
+
/** Bytes arriving from the far end. */
|
|
730
|
+
deliver(bytes: Uint8Array): void;
|
|
731
|
+
/** The far end went away; let the server's stream end cleanly. */
|
|
732
|
+
peerClosed(): void;
|
|
733
|
+
setNoDelay(): this;
|
|
734
|
+
setKeepAlive(): this;
|
|
735
|
+
setTimeout(_milliseconds: number, callback?: () => void): this;
|
|
736
|
+
destroySoon(): void;
|
|
737
|
+
ref(): this;
|
|
738
|
+
unref(): this;
|
|
739
|
+
}
|
|
686
740
|
declare class VirtualHttpServer extends EventEmitter {
|
|
687
741
|
private readonly router;
|
|
688
742
|
readonly owner: string;
|
|
@@ -716,6 +770,28 @@ declare class VirtualHttpRouter {
|
|
|
716
770
|
closeOwner(owner: string): void;
|
|
717
771
|
closeAll(): void;
|
|
718
772
|
request(port: number, init?: VirtualRequestInit): Promise<RuntimeHttpResponse>;
|
|
773
|
+
/**
|
|
774
|
+
* Open a connection that leaves HTTP behind — a WebSocket, in practice.
|
|
775
|
+
*
|
|
776
|
+
* The counterpart to {@link request}, and the thing whose absence made a dev
|
|
777
|
+
* server look broken. Every Node WebSocket library is built the same way: it
|
|
778
|
+
* hands `http.Server` an `upgrade` listener and waits. Nothing here ever
|
|
779
|
+
* emitted one, so `ws` sat holding a server that could not receive a single
|
|
780
|
+
* connection, and Vite lost the channel it uses to tell a page to reload —
|
|
781
|
+
* which is the only way it can recover after re-optimizing dependencies.
|
|
782
|
+
*
|
|
783
|
+
* Null means there is nothing to connect to: no server on the port, or a
|
|
784
|
+
* server that never asked for upgrades. Both are refusals the caller should
|
|
785
|
+
* report rather than wait out.
|
|
786
|
+
*/
|
|
787
|
+
connect(port: number, init: VirtualRequestInit, peer: VirtualSocketPeer): VirtualConnection | null;
|
|
788
|
+
}
|
|
789
|
+
/** A connection handed back by {@link VirtualHttpRouter.connect}. */
|
|
790
|
+
interface VirtualConnection {
|
|
791
|
+
/** Bytes from the far end, into the container. */
|
|
792
|
+
send(bytes: Uint8Array): void;
|
|
793
|
+
/** The far end went away. */
|
|
794
|
+
close(): void;
|
|
719
795
|
}
|
|
720
796
|
|
|
721
797
|
interface CoreModulesOptions {
|
|
@@ -1029,6 +1105,7 @@ declare class LocalRuntimePod implements RuntimePod {
|
|
|
1029
1105
|
*/
|
|
1030
1106
|
private settle;
|
|
1031
1107
|
request(_port: number, _init?: Record<string, unknown>): Promise<RuntimeHttpResponse>;
|
|
1108
|
+
connect(port: number, init: Record<string, unknown>, peer: RuntimeSocketPeer): RuntimeConnection | null;
|
|
1032
1109
|
snapshot(): MemoryVolumeSnapshotEntry[];
|
|
1033
1110
|
restore(snapshot: unknown): Promise<void>;
|
|
1034
1111
|
teardown(): void;
|
|
@@ -1076,6 +1153,8 @@ declare class WorkerRuntimePod extends LocalRuntimePod {
|
|
|
1076
1153
|
private runChildToCompletion;
|
|
1077
1154
|
private readonly proxies;
|
|
1078
1155
|
private readonly waiting;
|
|
1156
|
+
/** Upgraded connections, by the id the Worker knows them as. */
|
|
1157
|
+
private readonly upgraded;
|
|
1079
1158
|
private nextRequestId;
|
|
1080
1159
|
/**
|
|
1081
1160
|
* Register a stand-in for a server that is actually running in the Worker.
|
|
@@ -1085,6 +1164,14 @@ declare class WorkerRuntimePod extends LocalRuntimePod {
|
|
|
1085
1164
|
*/
|
|
1086
1165
|
private proxyPort;
|
|
1087
1166
|
private forward;
|
|
1167
|
+
/**
|
|
1168
|
+
* Tunnel one upgraded connection to the server that actually holds the port.
|
|
1169
|
+
*
|
|
1170
|
+
* Unlike {@link forward} there is no reply to wait for: both ends write
|
|
1171
|
+
* whenever they have something, until one of them stops. The id is what ties
|
|
1172
|
+
* the two directions together across the Worker boundary.
|
|
1173
|
+
*/
|
|
1174
|
+
private upgrade;
|
|
1088
1175
|
private settleProxied;
|
|
1089
1176
|
private closeProxies;
|
|
1090
1177
|
teardown(): void;
|
|
@@ -1465,6 +1552,19 @@ interface PreviewOptions {
|
|
|
1465
1552
|
* the moment, and it can stop after a few attempts rather than looping.
|
|
1466
1553
|
*/
|
|
1467
1554
|
onStale?: () => void;
|
|
1555
|
+
/**
|
|
1556
|
+
* Let pages inside the preview open WebSockets to the container.
|
|
1557
|
+
*
|
|
1558
|
+
* On by default. A service worker cannot answer a WebSocket handshake, so
|
|
1559
|
+
* previewed documents are given a `WebSocket` that tunnels through this page
|
|
1560
|
+
* instead — which means the HTML they are served is modified on the way
|
|
1561
|
+
* through, one `<script>` at the top of `<head>`.
|
|
1562
|
+
*
|
|
1563
|
+
* Turn it off to serve documents byte for byte. The cost is that a dev
|
|
1564
|
+
* server loses its HMR channel, and with it the `full-reload` it sends after
|
|
1565
|
+
* re-optimizing dependencies; {@link onStale} is the fallback for that.
|
|
1566
|
+
*/
|
|
1567
|
+
websocket?: boolean;
|
|
1468
1568
|
}
|
|
1469
1569
|
interface Preview {
|
|
1470
1570
|
/** The URL an iframe should be pointed at to see `port`. */
|
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,7 +28987,7 @@ 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;
|
|
@@ -28809,6 +28996,56 @@ async function createPreview(box, options = {}) {
|
|
|
28809
28996
|
};
|
|
28810
28997
|
navigator.serviceWorker.addEventListener("message", onWorkerMessage);
|
|
28811
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);
|
|
28812
29049
|
const base2 = registration.scope.replace(/\/$/, "");
|
|
28813
29050
|
const urlFor = (port) => `${base2}/__sbx__/${port}/`;
|
|
28814
29051
|
return {
|
|
@@ -28822,6 +29059,9 @@ async function createPreview(box, options = {}) {
|
|
|
28822
29059
|
},
|
|
28823
29060
|
dispose: async () => {
|
|
28824
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();
|
|
28825
29065
|
channel?.port1.close();
|
|
28826
29066
|
channel = null;
|
|
28827
29067
|
await registration.unregister();
|