@seanhogg/builderforce-webcontainers-core 2026.10.7 → 2026.10.8
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/dist/archive.d.ts +2 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +6 -4
- package/dist/archive.js.map +1 -1
- package/dist/html.d.ts +2 -0
- package/dist/html.d.ts.map +1 -1
- package/dist/html.js +5 -3
- package/dist/html.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/node/http/client.d.ts +3 -1
- package/dist/node/http/client.d.ts.map +1 -1
- package/dist/node/http/client.js +74 -5
- package/dist/node/http/client.js.map +1 -1
- package/dist/node/http/index.d.ts.map +1 -1
- package/dist/node/http/index.js +2 -1
- package/dist/node/http/index.js.map +1 -1
- package/dist/node/http/messages.d.ts +87 -0
- package/dist/node/http/messages.d.ts.map +1 -0
- package/dist/node/http/messages.js +146 -0
- package/dist/node/http/messages.js.map +1 -0
- package/dist/node/http/server.d.ts +13 -98
- package/dist/node/http/server.d.ts.map +1 -1
- package/dist/node/http/server.js +34 -202
- package/dist/node/http/server.js.map +1 -1
- package/dist/node/http/serverResponse.d.ts +42 -0
- package/dist/node/http/serverResponse.d.ts.map +1 -0
- package/dist/node/http/serverResponse.js +89 -0
- package/dist/node/http/serverResponse.js.map +1 -0
- package/dist/node/http/upgradeSocket.d.ts +34 -0
- package/dist/node/http/upgradeSocket.d.ts.map +1 -0
- package/dist/node/http/upgradeSocket.js +73 -0
- package/dist/node/http/upgradeSocket.js.map +1 -0
- package/dist/node/runtime.js +2 -2
- package/dist/node/runtime.js.map +1 -1
- package/dist/previewBridge.d.ts +42 -0
- package/dist/previewBridge.d.ts.map +1 -0
- package/dist/previewBridge.js +192 -0
- package/dist/previewBridge.js.map +1 -0
- package/dist/shell/ast.d.ts +101 -0
- package/dist/shell/ast.d.ts.map +1 -0
- package/dist/shell/ast.js +7 -0
- package/dist/shell/ast.js.map +1 -0
- package/dist/shell/builtins.d.ts +14 -0
- package/dist/shell/builtins.d.ts.map +1 -1
- package/dist/shell/builtins.js +87 -7
- package/dist/shell/builtins.js.map +1 -1
- package/dist/shell/compound.d.ts +30 -0
- package/dist/shell/compound.d.ts.map +1 -0
- package/dist/shell/compound.js +73 -0
- package/dist/shell/compound.js.map +1 -0
- package/dist/shell/exec.d.ts +27 -6
- package/dist/shell/exec.d.ts.map +1 -1
- package/dist/shell/exec.js +127 -41
- package/dist/shell/exec.js.map +1 -1
- package/dist/shell/expand.d.ts +5 -1
- package/dist/shell/expand.d.ts.map +1 -1
- package/dist/shell/expand.js +7 -5
- package/dist/shell/expand.js.map +1 -1
- package/dist/shell/lineReader.d.ts +9 -0
- package/dist/shell/lineReader.d.ts.map +1 -0
- package/dist/shell/lineReader.js +42 -0
- package/dist/shell/lineReader.js.map +1 -0
- package/dist/shell/packageManagers.d.ts +20 -0
- package/dist/shell/packageManagers.d.ts.map +1 -0
- package/dist/shell/packageManagers.js +74 -0
- package/dist/shell/packageManagers.js.map +1 -0
- package/dist/shell/parse.d.ts +16 -66
- package/dist/shell/parse.d.ts.map +1 -1
- package/dist/shell/parse.js +244 -243
- package/dist/shell/parse.js.map +1 -1
- package/dist/shell/program.d.ts.map +1 -1
- package/dist/shell/program.js +2 -1
- package/dist/shell/program.js.map +1 -1
- package/dist/shell/testCommand.d.ts +9 -0
- package/dist/shell/testCommand.d.ts.map +1 -0
- package/dist/shell/testCommand.js +105 -0
- package/dist/shell/testCommand.js.map +1 -0
- package/dist/shell/tokenize.d.ts +25 -0
- package/dist/shell/tokenize.d.ts.map +1 -0
- package/dist/shell/tokenize.js +279 -0
- package/dist/shell/tokenize.js.map +1 -0
- package/dist/system/cookieJar.d.ts +59 -0
- package/dist/system/cookieJar.d.ts.map +1 -0
- package/dist/system/cookieJar.js +187 -0
- package/dist/system/cookieJar.js.map +1 -0
- package/dist/system/httpStream.d.ts +40 -0
- package/dist/system/httpStream.d.ts.map +1 -0
- package/dist/system/httpStream.js +156 -0
- package/dist/system/httpStream.js.map +1 -0
- package/dist/system/index.d.ts +9 -1
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +4 -0
- package/dist/system/index.js.map +1 -1
- package/dist/system/kernel.d.ts +10 -2
- package/dist/system/kernel.d.ts.map +1 -1
- package/dist/system/kernel.js +12 -3
- package/dist/system/kernel.js.map +1 -1
- package/dist/system/ports.d.ts +9 -4
- package/dist/system/ports.d.ts.map +1 -1
- package/dist/system/ports.js +21 -2
- package/dist/system/ports.js.map +1 -1
- package/dist/system/programs.d.ts +1 -1
- package/dist/system/programs.d.ts.map +1 -1
- package/dist/system/programs.js +2 -0
- package/dist/system/programs.js.map +1 -1
- package/dist/system/types.d.ts +36 -6
- package/dist/system/types.d.ts.map +1 -1
- package/dist/system/webSocketClient.d.ts +37 -0
- package/dist/system/webSocketClient.d.ts.map +1 -0
- package/dist/system/webSocketClient.js +152 -0
- package/dist/system/webSocketClient.js.map +1 -0
- package/dist/system/webSocketFrames.d.ts +39 -0
- package/dist/system/webSocketFrames.d.ts.map +1 -0
- package/dist/system/webSocketFrames.js +117 -0
- package/dist/system/webSocketFrames.js.map +1 -0
- package/package.json +1 -1
- package/src/archive.ts +8 -4
- package/src/html.ts +5 -3
- package/src/index.ts +2 -1
- package/src/node/http/client.ts +69 -6
- package/src/node/http/index.ts +2 -1
- package/src/node/http/messages.ts +150 -0
- package/src/node/http/server.ts +37 -202
- package/src/node/http/serverResponse.ts +98 -0
- package/src/node/http/upgradeSocket.ts +79 -0
- package/src/node/runtime.ts +2 -2
- package/src/previewBridge.ts +194 -0
- package/src/shell/ast.ts +85 -0
- package/src/shell/builtins.ts +97 -7
- package/src/shell/compound.ts +89 -0
- package/src/shell/exec.ts +128 -40
- package/src/shell/expand.ts +8 -6
- package/src/shell/lineReader.ts +46 -0
- package/src/shell/packageManagers.ts +75 -0
- package/src/shell/parse.ts +233 -236
- package/src/shell/program.ts +2 -1
- package/src/shell/testCommand.ts +105 -0
- package/src/shell/tokenize.ts +257 -0
- package/src/system/cookieJar.ts +187 -0
- package/src/system/httpStream.ts +168 -0
- package/src/system/index.ts +24 -1
- package/src/system/kernel.ts +16 -4
- package/src/system/ports.ts +23 -4
- package/src/system/programs.ts +3 -1
- package/src/system/types.ts +40 -6
- package/src/system/webSocketClient.ts +177 -0
- package/src/system/webSocketFrames.ts +118 -0
package/dist/system/ports.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { bufferResponse, createChannelPair } from './httpStream.js';
|
|
1
2
|
const EPHEMERAL_START = 49152;
|
|
2
3
|
export class PortRegistry {
|
|
3
4
|
handlers = new Map();
|
|
5
|
+
upgrades = new Map();
|
|
4
6
|
listeners = new Set();
|
|
5
7
|
nextEphemeral = EPHEMERAL_START;
|
|
6
|
-
listen(port, handler) {
|
|
8
|
+
listen(port, handler, upgrade) {
|
|
7
9
|
let actual = port;
|
|
8
10
|
if (!actual) {
|
|
9
11
|
while (this.handlers.has(this.nextEphemeral))
|
|
@@ -14,6 +16,8 @@ export class PortRegistry {
|
|
|
14
16
|
throw Object.assign(new Error(`listen EADDRINUSE: address already in use :::${actual}`), { code: 'EADDRINUSE', errno: -98, syscall: 'listen', address: '::', port: actual });
|
|
15
17
|
}
|
|
16
18
|
this.handlers.set(actual, handler);
|
|
19
|
+
if (upgrade)
|
|
20
|
+
this.upgrades.set(actual, upgrade);
|
|
17
21
|
this.emit({ type: 'open', port: actual });
|
|
18
22
|
let open = true;
|
|
19
23
|
return {
|
|
@@ -23,6 +27,7 @@ export class PortRegistry {
|
|
|
23
27
|
return;
|
|
24
28
|
open = false;
|
|
25
29
|
this.handlers.delete(actual);
|
|
30
|
+
this.upgrades.delete(actual);
|
|
26
31
|
this.emit({ type: 'close', port: actual });
|
|
27
32
|
},
|
|
28
33
|
};
|
|
@@ -33,11 +38,25 @@ export class PortRegistry {
|
|
|
33
38
|
ports() {
|
|
34
39
|
return [...this.handlers.keys()].sort((a, b) => a - b);
|
|
35
40
|
}
|
|
36
|
-
/** Dispatch to the server on `port`;
|
|
41
|
+
/** Dispatch to the server on `port`; resolves with the head as soon as it is written, the body streaming. Undefined when nothing listens there. */
|
|
37
42
|
async request(port, request) {
|
|
38
43
|
const handler = this.handlers.get(port);
|
|
39
44
|
return handler ? handler(request) : undefined;
|
|
40
45
|
}
|
|
46
|
+
/** {@link request}, waiting for the whole body. */
|
|
47
|
+
async requestBuffered(port, request) {
|
|
48
|
+
const response = await this.request(port, request);
|
|
49
|
+
return response && bufferResponse(response);
|
|
50
|
+
}
|
|
51
|
+
/** Open an upgraded connection to the server on `port`; returns the client end, or undefined when nothing there takes upgrades. */
|
|
52
|
+
upgrade(port, request) {
|
|
53
|
+
const upgrade = this.upgrades.get(port);
|
|
54
|
+
if (!upgrade)
|
|
55
|
+
return undefined;
|
|
56
|
+
const [client, server] = createChannelPair();
|
|
57
|
+
upgrade(request, server);
|
|
58
|
+
return client;
|
|
59
|
+
}
|
|
41
60
|
watch(listener) {
|
|
42
61
|
this.listeners.add(listener);
|
|
43
62
|
return () => this.listeners.delete(listener);
|
package/dist/system/ports.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/system/ports.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ports.js","sourceRoot":"","sources":["../../src/system/ports.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The command table: every program a process can be. Hosts start from
|
|
3
3
|
* `defaultPrograms()` and may add or replace entries (a custom `git`, say).
|
|
4
4
|
*
|
|
5
|
-
* `npm`/`npx` live in the shell (they run scripts through it), so spawning them
|
|
5
|
+
* `npm`/`npx`/`pnpm`/`yarn` live in the shell (they run scripts through it), so spawning them
|
|
6
6
|
* directly runs them through a one-shot shell; coreutils run standalone.
|
|
7
7
|
*/
|
|
8
8
|
import type { Program } from './types.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../../src/system/programs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAY1C,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"programs.d.ts","sourceRoot":"","sources":["../../src/system/programs.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAY1C,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazD"}
|
package/dist/system/programs.js
CHANGED
|
@@ -14,6 +14,8 @@ export function defaultPrograms() {
|
|
|
14
14
|
bash: shellProgram,
|
|
15
15
|
npm: viaShell('npm'),
|
|
16
16
|
npx: viaShell('npx'),
|
|
17
|
+
pnpm: viaShell('pnpm'),
|
|
18
|
+
yarn: viaShell('yarn'),
|
|
17
19
|
};
|
|
18
20
|
for (const [name, command] of Object.entries(COREUTILS))
|
|
19
21
|
programs[name] = asProgram(command, name);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"programs.js","sourceRoot":"","sources":["../../src/system/programs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAExG,SAAS,QAAQ,CAAC,OAAe;IAC/B,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,QAAQ,GAA4B;QACxC,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC;QACpB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"programs.js","sourceRoot":"","sources":["../../src/system/programs.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhD,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;AAExG,SAAS,QAAQ,CAAC,OAAe;IAC/B,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;AACtG,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,MAAM,QAAQ,GAA4B;QACxC,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,YAAY;QAChB,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC;QACpB,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC;QACpB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC;QACtB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC;KACvB,CAAC;IACF,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACnG,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/system/types.d.ts
CHANGED
|
@@ -48,25 +48,55 @@ export interface HttpRequestData {
|
|
|
48
48
|
headers: Record<string, string>;
|
|
49
49
|
body: Uint8Array | null;
|
|
50
50
|
}
|
|
51
|
-
export interface
|
|
51
|
+
export interface HttpResponseHead {
|
|
52
52
|
status: number;
|
|
53
53
|
statusText?: string;
|
|
54
54
|
headers: Record<string, string | string[]>;
|
|
55
|
+
}
|
|
56
|
+
/** A whole response, body buffered (`Kernel.request`, tests). */
|
|
57
|
+
export interface HttpResponseData extends HttpResponseHead {
|
|
55
58
|
body: Uint8Array;
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
/**
|
|
61
|
+
* A response as the server produces it: the head as soon as it is written, then
|
|
62
|
+
* the body chunk by chunk (Server-Sent Events, chunked progress). Cancelling the
|
|
63
|
+
* body hangs up, which the server sees as the client closing the connection.
|
|
64
|
+
*/
|
|
65
|
+
export interface HttpStreamResponse extends HttpResponseHead {
|
|
66
|
+
body: ReadableStream<Uint8Array>;
|
|
67
|
+
}
|
|
68
|
+
export type HttpHandler = (request: HttpRequestData) => Promise<HttpStreamResponse>;
|
|
69
|
+
/**
|
|
70
|
+
* One end of a raw byte pipe — an upgraded connection. The server end carries
|
|
71
|
+
* whatever the server writes (a WebSocket server writes its `101` reply and then
|
|
72
|
+
* frames), so packages like `ws` run their own framing over it unchanged.
|
|
73
|
+
*/
|
|
74
|
+
export interface ByteChannel {
|
|
75
|
+
send(data: Uint8Array): void;
|
|
76
|
+
/** Hang up; the other end hears `onClose`. */
|
|
77
|
+
close(): void;
|
|
78
|
+
onData(listener: (data: Uint8Array) => void): () => void;
|
|
79
|
+
/** The other end hung up. */
|
|
80
|
+
onClose(listener: () => void): () => void;
|
|
81
|
+
readonly closed: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** Take over a connection a client asked to upgrade (`Upgrade: websocket`); `socket` is the server end. */
|
|
84
|
+
export type UpgradeHandler = (request: HttpRequestData, socket: ByteChannel) => void;
|
|
58
85
|
export interface System {
|
|
59
86
|
fs: VirtualFileSystem;
|
|
60
87
|
/** Outbound network (registry, `http.request`, `fetch`). */
|
|
61
88
|
fetch(input: string, init?: RequestInit): Promise<Response>;
|
|
62
89
|
spawn(command: string, args: string[], options?: ProcessOptions): ChildHandle;
|
|
63
|
-
/**
|
|
64
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Register a virtual server; throws `EADDRINUSE`. Port 0 picks a free one.
|
|
92
|
+
* `upgrade` takes over connections that ask to upgrade (WebSockets). Returns the port and an unlisten.
|
|
93
|
+
*/
|
|
94
|
+
listen(port: number, handler: HttpHandler, upgrade?: UpgradeHandler): {
|
|
65
95
|
port: number;
|
|
66
96
|
close(): void;
|
|
67
97
|
};
|
|
68
|
-
/** Send a request to a virtual server in this runtime (`http.get('http://localhost:3000')`); undefined when nothing listens. */
|
|
69
|
-
loopback(port: number, request: HttpRequestData): Promise<
|
|
98
|
+
/** Send a request to a virtual server in this runtime (`http.get('http://localhost:3000')`); undefined when nothing listens. The body streams. */
|
|
99
|
+
loopback(port: number, request: HttpRequestData): Promise<HttpStreamResponse | undefined>;
|
|
70
100
|
/** Where a browser reaches a server on `port` (the preview URL), if known. */
|
|
71
101
|
serverUrl(port: number): string | undefined;
|
|
72
102
|
/** Tell the host a server is reachable without opening a port; used by the dev-server handoff. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/system/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACrE,kFAAkF;IAClF,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACzC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACzC,+FAA+F;IAC/F,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC9D,sDAAsD;IACtD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,2HAA2H;AAC3H,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxD,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACxD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,IAAI,IAAI,CAAC;IACnB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/system/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The client end of a WebSocket to a virtual server, over an upgraded
|
|
3
|
+
* {@link ByteChannel}: sends the opening handshake as an HTTP request, checks
|
|
4
|
+
* the server's `101` reply (whatever wrote it — `ws`, socket.io's engine, a
|
|
5
|
+
* hand-written `upgrade` handler), then speaks RFC 6455 frames. The host page
|
|
6
|
+
* uses it to carry a preview's `new WebSocket('ws://localhost:3000')`.
|
|
7
|
+
*/
|
|
8
|
+
import type { ByteChannel, HttpRequestData } from './types.js';
|
|
9
|
+
export interface WebSocketCloseInfo {
|
|
10
|
+
code: number;
|
|
11
|
+
reason: string;
|
|
12
|
+
wasClean: boolean;
|
|
13
|
+
}
|
|
14
|
+
export interface WebSocketConnection {
|
|
15
|
+
/** 0 connecting, 1 open, 2 closing, 3 closed — `WebSocket.readyState`'s values. */
|
|
16
|
+
readonly readyState: number;
|
|
17
|
+
onOpen(listener: (info: {
|
|
18
|
+
protocol: string;
|
|
19
|
+
}) => void): () => void;
|
|
20
|
+
onMessage(listener: (data: string | Uint8Array) => void): () => void;
|
|
21
|
+
/** Fires once; `wasClean` false and code 1006 when the connection dropped or the handshake failed. */
|
|
22
|
+
onClose(listener: (info: WebSocketCloseInfo) => void): () => void;
|
|
23
|
+
send(data: string | Uint8Array): void;
|
|
24
|
+
close(code?: number, reason?: string): void;
|
|
25
|
+
}
|
|
26
|
+
export interface WebSocketConnectOptions {
|
|
27
|
+
/** Path and query on the server (`/socket?room=1`). */
|
|
28
|
+
path: string;
|
|
29
|
+
protocols?: string[];
|
|
30
|
+
/** Extra request headers (`origin`, `cookie`). */
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
/** Give up on a close handshake the server never answers. Default 5000ms. */
|
|
33
|
+
closeTimeoutMs?: number;
|
|
34
|
+
}
|
|
35
|
+
/** Open a WebSocket through `upgrade` (`kernel.upgrade` / `processHost.upgrade` bound to a port). */
|
|
36
|
+
export declare function connectWebSocket(upgrade: (request: HttpRequestData) => ByteChannel | undefined, options: WebSocketConnectOptions): WebSocketConnection;
|
|
37
|
+
//# sourceMappingURL=webSocketClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocketClient.d.ts","sourceRoot":"","sources":["../../src/system/webSocketClient.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG/D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACnE,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACrE,sGAAsG;IACtG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAClE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAsBD,qGAAqG;AACrG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,WAAW,GAAG,SAAS,EAAE,OAAO,EAAE,uBAAuB,GAAG,mBAAmB,CAuGtJ"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { FrameDecoder, Opcode, decodeClosePayload, encodeClosePayload, encodeFrame, webSocketAccept, webSocketKey } from './webSocketFrames.js';
|
|
2
|
+
const HEAD_END = [13, 10, 13, 10];
|
|
3
|
+
function indexOfHeadEnd(bytes) {
|
|
4
|
+
outer: for (let i = 0; i + 3 < bytes.length; i++) {
|
|
5
|
+
for (let j = 0; j < 4; j++)
|
|
6
|
+
if (bytes[i + j] !== HEAD_END[j])
|
|
7
|
+
continue outer;
|
|
8
|
+
return i;
|
|
9
|
+
}
|
|
10
|
+
return -1;
|
|
11
|
+
}
|
|
12
|
+
function parseHead(text) {
|
|
13
|
+
const [statusLine = '', ...lines] = text.split('\r\n');
|
|
14
|
+
const headers = {};
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
const colon = line.indexOf(':');
|
|
17
|
+
if (colon > 0)
|
|
18
|
+
headers[line.slice(0, colon).trim().toLowerCase()] = line.slice(colon + 1).trim();
|
|
19
|
+
}
|
|
20
|
+
return { status: Number(/^HTTP\/\d(?:\.\d)?\s+(\d{3})/.exec(statusLine)?.[1] ?? 0), headers };
|
|
21
|
+
}
|
|
22
|
+
/** Open a WebSocket through `upgrade` (`kernel.upgrade` / `processHost.upgrade` bound to a port). */
|
|
23
|
+
export function connectWebSocket(upgrade, options) {
|
|
24
|
+
const opens = new Set();
|
|
25
|
+
const messages = new Set();
|
|
26
|
+
const closes = new Set();
|
|
27
|
+
const key = webSocketKey();
|
|
28
|
+
const decoder = new FrameDecoder();
|
|
29
|
+
let state = 0;
|
|
30
|
+
let head = new Uint8Array(0);
|
|
31
|
+
let fragments = [];
|
|
32
|
+
let fragmentOpcode = 0;
|
|
33
|
+
let closeSent = false;
|
|
34
|
+
let closeTimer;
|
|
35
|
+
const headers = {
|
|
36
|
+
...options.headers,
|
|
37
|
+
connection: 'Upgrade',
|
|
38
|
+
upgrade: 'websocket',
|
|
39
|
+
'sec-websocket-version': '13',
|
|
40
|
+
'sec-websocket-key': key,
|
|
41
|
+
};
|
|
42
|
+
if (options.protocols?.length)
|
|
43
|
+
headers['sec-websocket-protocol'] = options.protocols.join(', ');
|
|
44
|
+
const channel = upgrade({ method: 'GET', url: options.path, headers, body: null });
|
|
45
|
+
const finish = (info) => {
|
|
46
|
+
if (state === 3)
|
|
47
|
+
return;
|
|
48
|
+
state = 3;
|
|
49
|
+
clearTimeout(closeTimer);
|
|
50
|
+
channel?.close();
|
|
51
|
+
for (const listener of closes)
|
|
52
|
+
listener(info);
|
|
53
|
+
};
|
|
54
|
+
const fail = () => finish({ code: 1006, reason: '', wasClean: false });
|
|
55
|
+
const sendFrame = (opcode, payload) => channel?.send(encodeFrame(opcode, payload, { mask: true }));
|
|
56
|
+
const onFrame = (opcode, fin, payload) => {
|
|
57
|
+
if (opcode === Opcode.ping)
|
|
58
|
+
return sendFrame(Opcode.pong, payload);
|
|
59
|
+
if (opcode === Opcode.pong)
|
|
60
|
+
return;
|
|
61
|
+
if (opcode === Opcode.close) {
|
|
62
|
+
const { code, reason } = decodeClosePayload(payload);
|
|
63
|
+
if (!closeSent) {
|
|
64
|
+
closeSent = true;
|
|
65
|
+
sendFrame(Opcode.close, code === 1005 ? new Uint8Array() : encodeClosePayload(code));
|
|
66
|
+
}
|
|
67
|
+
return finish({ code, reason, wasClean: true });
|
|
68
|
+
}
|
|
69
|
+
if (opcode !== Opcode.continuation) {
|
|
70
|
+
fragmentOpcode = opcode;
|
|
71
|
+
fragments = [];
|
|
72
|
+
}
|
|
73
|
+
fragments.push(payload);
|
|
74
|
+
if (!fin)
|
|
75
|
+
return;
|
|
76
|
+
const whole = fragments.length === 1 ? fragments[0] : joinChunks(fragments);
|
|
77
|
+
fragments = [];
|
|
78
|
+
const data = fragmentOpcode === Opcode.text ? new TextDecoder().decode(whole) : whole;
|
|
79
|
+
for (const listener of messages)
|
|
80
|
+
listener(data);
|
|
81
|
+
};
|
|
82
|
+
const onBytes = (bytes) => {
|
|
83
|
+
if (state === 3)
|
|
84
|
+
return;
|
|
85
|
+
let rest = bytes;
|
|
86
|
+
if (state === 0) {
|
|
87
|
+
head = joinChunks([head, bytes]);
|
|
88
|
+
const end = indexOfHeadEnd(head);
|
|
89
|
+
if (end < 0)
|
|
90
|
+
return;
|
|
91
|
+
const reply = parseHead(new TextDecoder().decode(head.subarray(0, end)));
|
|
92
|
+
rest = head.subarray(end + 4);
|
|
93
|
+
if (reply.status !== 101 || reply.headers['sec-websocket-accept'] !== webSocketAccept(key))
|
|
94
|
+
return fail();
|
|
95
|
+
state = 1;
|
|
96
|
+
const protocol = reply.headers['sec-websocket-protocol'] ?? '';
|
|
97
|
+
for (const listener of opens)
|
|
98
|
+
listener({ protocol });
|
|
99
|
+
}
|
|
100
|
+
for (const frame of decoder.push(rest)) {
|
|
101
|
+
onFrame(frame.opcode, frame.fin, frame.payload);
|
|
102
|
+
if (state === 3)
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
if (!channel)
|
|
107
|
+
queueMicrotask(fail);
|
|
108
|
+
else {
|
|
109
|
+
channel.onData(onBytes);
|
|
110
|
+
channel.onClose(fail);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
get readyState() {
|
|
114
|
+
return state;
|
|
115
|
+
},
|
|
116
|
+
onOpen: (listener) => subscribe(opens, listener),
|
|
117
|
+
onMessage: (listener) => subscribe(messages, listener),
|
|
118
|
+
onClose: (listener) => subscribe(closes, listener),
|
|
119
|
+
send(data) {
|
|
120
|
+
if (state !== 1)
|
|
121
|
+
return;
|
|
122
|
+
if (typeof data === 'string')
|
|
123
|
+
sendFrame(Opcode.text, new TextEncoder().encode(data));
|
|
124
|
+
else
|
|
125
|
+
sendFrame(Opcode.binary, data);
|
|
126
|
+
},
|
|
127
|
+
close(code, reason) {
|
|
128
|
+
if (state === 0)
|
|
129
|
+
return fail();
|
|
130
|
+
if (state !== 1)
|
|
131
|
+
return;
|
|
132
|
+
state = 2;
|
|
133
|
+
closeSent = true;
|
|
134
|
+
sendFrame(Opcode.close, encodeClosePayload(code ?? 1000, reason));
|
|
135
|
+
closeTimer = setTimeout(fail, options.closeTimeoutMs ?? 5000);
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function subscribe(set, listener) {
|
|
140
|
+
set.add(listener);
|
|
141
|
+
return () => set.delete(listener);
|
|
142
|
+
}
|
|
143
|
+
function joinChunks(chunks) {
|
|
144
|
+
const out = new Uint8Array(chunks.reduce((n, c) => n + c.byteLength, 0));
|
|
145
|
+
let offset = 0;
|
|
146
|
+
for (const chunk of chunks) {
|
|
147
|
+
out.set(chunk, offset);
|
|
148
|
+
offset += chunk.byteLength;
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=webSocketClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocketClient.js","sourceRoot":"","sources":["../../src/system/webSocketClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export declare const WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
|
2
|
+
export declare const Opcode: {
|
|
3
|
+
readonly continuation: 0;
|
|
4
|
+
readonly text: 1;
|
|
5
|
+
readonly binary: 2;
|
|
6
|
+
readonly close: 8;
|
|
7
|
+
readonly ping: 9;
|
|
8
|
+
readonly pong: 10;
|
|
9
|
+
};
|
|
10
|
+
export interface WebSocketFrame {
|
|
11
|
+
fin: boolean;
|
|
12
|
+
opcode: number;
|
|
13
|
+
/** Unmasked. */
|
|
14
|
+
payload: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
17
|
+
/** `Sec-WebSocket-Accept` for a client's `Sec-WebSocket-Key`. */
|
|
18
|
+
export declare function webSocketAccept(key: string): string;
|
|
19
|
+
/** A fresh `Sec-WebSocket-Key` (16 random bytes, base64). */
|
|
20
|
+
export declare function webSocketKey(): string;
|
|
21
|
+
/** One frame. Clients must mask (`mask: true` picks a random key); servers must not. */
|
|
22
|
+
export declare function encodeFrame(opcode: number, payload: Uint8Array, options?: {
|
|
23
|
+
mask?: boolean | Uint8Array;
|
|
24
|
+
fin?: boolean;
|
|
25
|
+
}): Uint8Array;
|
|
26
|
+
/** A close frame's payload: the status code and a UTF-8 reason. */
|
|
27
|
+
export declare function encodeClosePayload(code?: number, reason?: string): Uint8Array;
|
|
28
|
+
/** Status 1005 ("no status received") when the payload is empty. */
|
|
29
|
+
export declare function decodeClosePayload(payload: Uint8Array): {
|
|
30
|
+
code: number;
|
|
31
|
+
reason: string;
|
|
32
|
+
};
|
|
33
|
+
/** Splits a byte stream into frames, whatever the chunking. */
|
|
34
|
+
export declare class FrameDecoder {
|
|
35
|
+
private buffer;
|
|
36
|
+
push(bytes: Uint8Array): WebSocketFrame[];
|
|
37
|
+
private next;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=webSocketFrames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocketFrames.d.ts","sourceRoot":"","sources":["../../src/system/webSocketFrames.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,cAAc,yCAAyC,CAAC;AAErE,eAAO,MAAM,MAAM;;;;;;;CAA2F,CAAC;AAE/G,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB;IAChB,OAAO,EAAE,UAAU,CAAC;CACrB;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIvD;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wFAAwF;AACxF,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,UAAU,CAmBzI;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,UAAU,CAOzE;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAGxF;AAED,+DAA+D;AAC/D,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAqB;IAEnC,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,EAAE;IAezC,OAAO,CAAC,IAAI;CAyBb"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The WebSocket wire format (RFC 6455): the opening handshake's accept key and
|
|
3
|
+
* frame encoding/decoding. Runtime-agnostic, so the same codec drives the
|
|
4
|
+
* client end in the host page (bridging a preview's `WebSocket`) and tests that
|
|
5
|
+
* speak to a server's `upgrade` handler byte for byte.
|
|
6
|
+
*/
|
|
7
|
+
import { hashAlgorithm } from '../node/hashes.js';
|
|
8
|
+
export const WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
|
|
9
|
+
export const Opcode = { continuation: 0x0, text: 0x1, binary: 0x2, close: 0x8, ping: 0x9, pong: 0xa };
|
|
10
|
+
export function bytesToBase64(bytes) {
|
|
11
|
+
let binary = '';
|
|
12
|
+
for (let i = 0; i < bytes.length; i++)
|
|
13
|
+
binary += String.fromCharCode(bytes[i]);
|
|
14
|
+
return btoa(binary);
|
|
15
|
+
}
|
|
16
|
+
/** `Sec-WebSocket-Accept` for a client's `Sec-WebSocket-Key`. */
|
|
17
|
+
export function webSocketAccept(key) {
|
|
18
|
+
return bytesToBase64(hashAlgorithm('sha1').digest(new TextEncoder().encode(key + WEBSOCKET_GUID)));
|
|
19
|
+
}
|
|
20
|
+
/** A fresh `Sec-WebSocket-Key` (16 random bytes, base64). */
|
|
21
|
+
export function webSocketKey() {
|
|
22
|
+
return bytesToBase64(globalThis.crypto.getRandomValues(new Uint8Array(16)));
|
|
23
|
+
}
|
|
24
|
+
/** One frame. Clients must mask (`mask: true` picks a random key); servers must not. */
|
|
25
|
+
export function encodeFrame(opcode, payload, options = {}) {
|
|
26
|
+
const length = payload.byteLength;
|
|
27
|
+
const maskKey = options.mask === true ? globalThis.crypto.getRandomValues(new Uint8Array(4)) : options.mask || undefined;
|
|
28
|
+
const extended = length < 126 ? 0 : length <= 0xffff ? 2 : 8;
|
|
29
|
+
const headerLength = 2 + extended + (maskKey ? 4 : 0);
|
|
30
|
+
const frame = new Uint8Array(headerLength + length);
|
|
31
|
+
const view = new DataView(frame.buffer);
|
|
32
|
+
frame[0] = (options.fin === false ? 0 : 0x80) | (opcode & 0x0f);
|
|
33
|
+
frame[1] = (maskKey ? 0x80 : 0) | (extended === 0 ? length : extended === 2 ? 126 : 127);
|
|
34
|
+
if (extended === 2)
|
|
35
|
+
view.setUint16(2, length);
|
|
36
|
+
else if (extended === 8) {
|
|
37
|
+
view.setUint32(2, Math.floor(length / 2 ** 32));
|
|
38
|
+
view.setUint32(6, length >>> 0);
|
|
39
|
+
}
|
|
40
|
+
if (maskKey) {
|
|
41
|
+
frame.set(maskKey, 2 + extended);
|
|
42
|
+
for (let i = 0; i < length; i++)
|
|
43
|
+
frame[headerLength + i] = payload[i] ^ maskKey[i & 3];
|
|
44
|
+
}
|
|
45
|
+
else
|
|
46
|
+
frame.set(payload, headerLength);
|
|
47
|
+
return frame;
|
|
48
|
+
}
|
|
49
|
+
/** A close frame's payload: the status code and a UTF-8 reason. */
|
|
50
|
+
export function encodeClosePayload(code, reason = '') {
|
|
51
|
+
if (code === undefined)
|
|
52
|
+
return new Uint8Array();
|
|
53
|
+
const text = new TextEncoder().encode(reason);
|
|
54
|
+
const out = new Uint8Array(2 + text.byteLength);
|
|
55
|
+
new DataView(out.buffer).setUint16(0, code);
|
|
56
|
+
out.set(text, 2);
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
/** Status 1005 ("no status received") when the payload is empty. */
|
|
60
|
+
export function decodeClosePayload(payload) {
|
|
61
|
+
if (payload.byteLength < 2)
|
|
62
|
+
return { code: 1005, reason: '' };
|
|
63
|
+
return { code: new DataView(payload.buffer, payload.byteOffset, payload.byteLength).getUint16(0), reason: new TextDecoder().decode(payload.subarray(2)) };
|
|
64
|
+
}
|
|
65
|
+
/** Splits a byte stream into frames, whatever the chunking. */
|
|
66
|
+
export class FrameDecoder {
|
|
67
|
+
buffer = new Uint8Array(0);
|
|
68
|
+
push(bytes) {
|
|
69
|
+
if (bytes.byteLength) {
|
|
70
|
+
const joined = new Uint8Array(this.buffer.byteLength + bytes.byteLength);
|
|
71
|
+
joined.set(this.buffer);
|
|
72
|
+
joined.set(bytes, this.buffer.byteLength);
|
|
73
|
+
this.buffer = joined;
|
|
74
|
+
}
|
|
75
|
+
const frames = [];
|
|
76
|
+
for (;;) {
|
|
77
|
+
const frame = this.next();
|
|
78
|
+
if (!frame)
|
|
79
|
+
return frames;
|
|
80
|
+
frames.push(frame);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
next() {
|
|
84
|
+
const b = this.buffer;
|
|
85
|
+
if (b.byteLength < 2)
|
|
86
|
+
return undefined;
|
|
87
|
+
const masked = (b[1] & 0x80) !== 0;
|
|
88
|
+
let length = b[1] & 0x7f;
|
|
89
|
+
let offset = 2;
|
|
90
|
+
const view = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
91
|
+
if (length === 126) {
|
|
92
|
+
if (b.byteLength < 4)
|
|
93
|
+
return undefined;
|
|
94
|
+
length = view.getUint16(2);
|
|
95
|
+
offset = 4;
|
|
96
|
+
}
|
|
97
|
+
else if (length === 127) {
|
|
98
|
+
if (b.byteLength < 10)
|
|
99
|
+
return undefined;
|
|
100
|
+
length = view.getUint32(2) * 2 ** 32 + view.getUint32(6);
|
|
101
|
+
offset = 10;
|
|
102
|
+
}
|
|
103
|
+
const maskKey = masked ? b.subarray(offset, offset + 4) : undefined;
|
|
104
|
+
if (masked)
|
|
105
|
+
offset += 4;
|
|
106
|
+
if (b.byteLength < offset + length)
|
|
107
|
+
return undefined;
|
|
108
|
+
const payload = b.slice(offset, offset + length);
|
|
109
|
+
if (maskKey)
|
|
110
|
+
for (let i = 0; i < payload.length; i++)
|
|
111
|
+
payload[i] = payload[i] ^ maskKey[i & 3];
|
|
112
|
+
const frame = { fin: (b[0] & 0x80) !== 0, opcode: b[0] & 0x0f, payload };
|
|
113
|
+
this.buffer = b.slice(offset + length);
|
|
114
|
+
return frame;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=webSocketFrames.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webSocketFrames.js","sourceRoot":"","sources":["../../src/system/webSocketFrames.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seanhogg/builderforce-webcontainers-core",
|
|
3
|
-
"version": "2026.10.
|
|
3
|
+
"version": "2026.10.8",
|
|
4
4
|
"description": "BuilderForce WebContainers — core. A runtime-agnostic in-browser dev server: virtual file system, module resolution, import rewriting to a package CDN, CSS/JSON/asset modules and project detection. No COOP/COEP required.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
package/src/archive.ts
CHANGED
|
@@ -9,6 +9,8 @@ import { VirtualFileSystem } from './vfs.js';
|
|
|
9
9
|
import { join } from './paths.js';
|
|
10
10
|
|
|
11
11
|
export interface ZipExportOptions {
|
|
12
|
+
/** Only these path prefixes (`node_modules`), relative to the root, no leading slash. Default: everything. */
|
|
13
|
+
include?: string[];
|
|
12
14
|
/** Path prefixes left out (`node_modules`, `dist`), relative to the root, no leading slash. */
|
|
13
15
|
exclude?: string[];
|
|
14
16
|
/** 0 (store, fastest) to 9. Default 6. */
|
|
@@ -18,8 +20,8 @@ export interface ZipExportOptions {
|
|
|
18
20
|
const encoder = new TextEncoder();
|
|
19
21
|
const strictUtf8 = new TextDecoder('utf-8', { fatal: true });
|
|
20
22
|
|
|
21
|
-
function
|
|
22
|
-
return
|
|
23
|
+
function under(relative: string, prefixes: string[]): boolean {
|
|
24
|
+
return prefixes.some((prefix) => {
|
|
23
25
|
const clean = prefix.replace(/^\/+|\/+$/g, '');
|
|
24
26
|
return relative === clean || relative.startsWith(clean + '/');
|
|
25
27
|
});
|
|
@@ -27,14 +29,16 @@ function excluded(relative: string, exclude: string[]): boolean {
|
|
|
27
29
|
|
|
28
30
|
export function exportZip(fs: VirtualFileSystem, options: ZipExportOptions = {}): Uint8Array {
|
|
29
31
|
const exclude = options.exclude ?? [];
|
|
32
|
+
const include = options.include;
|
|
33
|
+
const excluded = (relative: string) => (include !== undefined && !under(relative, include)) || under(relative, exclude);
|
|
30
34
|
const entries: Zippable = {};
|
|
31
35
|
for (const dir of fs.explicitDirectories()) {
|
|
32
36
|
const relative = dir.slice(1);
|
|
33
|
-
if (!excluded(relative
|
|
37
|
+
if (!excluded(relative) && fs.readdir(dir).length === 0) entries[relative + '/'] = new Uint8Array(0);
|
|
34
38
|
}
|
|
35
39
|
for (const path of fs.list()) {
|
|
36
40
|
const relative = path.slice(1);
|
|
37
|
-
if (excluded(relative
|
|
41
|
+
if (excluded(relative)) continue;
|
|
38
42
|
const contents = fs.readFile(path)!;
|
|
39
43
|
entries[relative] = typeof contents === 'string' ? encoder.encode(contents) : contents;
|
|
40
44
|
}
|