@statewalker/webrun-http-browser 0.6.5 → 0.7.0
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 +302 -52
- package/dist/core/data-calls.d.ts +17 -0
- package/dist/core/data-calls.d.ts.map +1 -1
- package/dist/core/service-worker-control.d.ts +3 -1
- package/dist/core/service-worker-control.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3906 -265
- package/dist/link/errors.d.ts +2 -0
- package/dist/link/errors.d.ts.map +1 -0
- package/dist/link/http-link.d.ts +58 -0
- package/dist/link/http-link.d.ts.map +1 -0
- package/dist/link/index.d.ts +4 -0
- package/dist/link/index.d.ts.map +1 -0
- package/dist/link/locks.d.ts +39 -0
- package/dist/link/locks.d.ts.map +1 -0
- package/dist/link/rendezvous.d.ts +21 -0
- package/dist/link/rendezvous.d.ts.map +1 -0
- package/dist/relay/index-sw.d.ts +12 -2
- package/dist/relay/index-sw.d.ts.map +1 -1
- package/dist/relay/index.d.ts +66 -8
- package/dist/relay/index.d.ts.map +1 -1
- package/dist/relay-sw.js +1160 -55
- package/dist/relay-worker.js +1161 -55
- package/dist/sw/handler-endpoint.d.ts +81 -0
- package/dist/sw/handler-endpoint.d.ts.map +1 -0
- package/dist/sw/handler-table.d.ts +53 -0
- package/dist/sw/handler-table.d.ts.map +1 -0
- package/dist/sw/http-sw-dispatcher.d.ts +162 -21
- package/dist/sw/http-sw-dispatcher.d.ts.map +1 -1
- package/dist/sw/index.d.ts +5 -0
- package/dist/sw/index.d.ts.map +1 -1
- package/dist/sw/protocol.d.ts +115 -0
- package/dist/sw/protocol.d.ts.map +1 -0
- package/dist/sw/sw-dispatcher.d.ts.map +1 -1
- package/dist/sw/sw-http-adapter.d.ts +199 -0
- package/dist/sw/sw-http-adapter.d.ts.map +1 -0
- package/dist/sw/sw-websocket.d.ts +16 -0
- package/dist/sw/sw-websocket.d.ts.map +1 -0
- package/dist/sw/worker-service.d.ts +45 -0
- package/dist/sw/worker-service.d.ts.map +1 -0
- package/dist/sw/worker-url.d.ts +9 -0
- package/dist/sw/worker-url.d.ts.map +1 -0
- package/dist/sw-worker.js +2075 -210
- package/dist/sw.d.ts +2 -0
- package/dist/sw.d.ts.map +1 -1
- package/dist/sw.js +3901 -531
- package/dist/worker.d.ts +10 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +3507 -0
- package/dist/ws/client.d.ts +73 -0
- package/dist/ws/client.d.ts.map +1 -0
- package/dist/ws/frames.d.ts +68 -0
- package/dist/ws/frames.d.ts.map +1 -0
- package/dist/ws/index.d.ts +6 -0
- package/dist/ws/index.d.ts.map +1 -0
- package/dist/ws/server.d.ts +53 -0
- package/dist/ws/server.d.ts.map +1 -0
- package/dist/ws/socket-core.d.ts +91 -0
- package/dist/ws/socket-core.d.ts.map +1 -0
- package/dist/ws/upgrade.d.ts +93 -0
- package/dist/ws/upgrade.d.ts.map +1 -0
- package/package.json +10 -5
- package/src/core/data-calls.ts +52 -2
- package/src/core/service-worker-control.ts +7 -1
- package/src/index.ts +3 -0
- package/src/link/errors.ts +3 -0
- package/src/link/http-link.ts +222 -0
- package/src/link/index.ts +3 -0
- package/src/link/locks.ts +97 -0
- package/src/link/rendezvous.ts +60 -0
- package/src/relay/index-sw.ts +84 -12
- package/src/relay/index.ts +203 -27
- package/src/sw/handler-endpoint.ts +278 -0
- package/src/sw/handler-table.ts +130 -0
- package/src/sw/http-sw-dispatcher.ts +649 -90
- package/src/sw/index.ts +5 -0
- package/src/sw/protocol.ts +103 -0
- package/src/sw/sw-dispatcher.ts +1 -20
- package/src/sw/sw-http-adapter.ts +613 -0
- package/src/sw/sw-websocket.ts +43 -0
- package/src/sw/worker-service.ts +133 -0
- package/src/sw/worker-url.ts +19 -0
- package/src/sw-worker.ts +8 -3
- package/src/sw.ts +2 -0
- package/src/worker.ts +9 -0
- package/src/ws/client.ts +346 -0
- package/src/ws/frames.ts +119 -0
- package/src/ws/index.ts +11 -0
- package/src/ws/server.ts +132 -0
- package/src/ws/socket-core.ts +251 -0
- package/src/ws/upgrade.ts +200 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type WsTimers } from "./frames.js";
|
|
2
|
+
/** What `connect` gives a socket once the server accepted it (the 101). */
|
|
3
|
+
export interface WebSocketConnection {
|
|
4
|
+
port: MessagePort;
|
|
5
|
+
/** The subprotocol the server picked, `""` for none. */
|
|
6
|
+
protocol: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Opens the transport of one socket: resolves once the server accepted the
|
|
10
|
+
* upgrade, rejects when it refused or could not be reached. Where the port
|
|
11
|
+
* comes from — a ServiceWorker, a relay, a handler in the same page — is the
|
|
12
|
+
* caller's business.
|
|
13
|
+
*/
|
|
14
|
+
export type WebSocketConnector = (url: string, protocols: string[]) => Promise<WebSocketConnection>;
|
|
15
|
+
export interface WebSocketClassOptions {
|
|
16
|
+
/** Ping period, in ms. Pongs missing for twice that close the socket with 1006. Default 10 s. */
|
|
17
|
+
heartbeatMs?: number;
|
|
18
|
+
/** Timer functions; see `WsTimers`. */
|
|
19
|
+
timers?: WsTimers;
|
|
20
|
+
/** How long `close()` waits for the server's echo, in ms. Default 5 s. */
|
|
21
|
+
closeTimeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
type Listener = ((event: Event) => void) | null;
|
|
24
|
+
/** The instance side of the class `createWebSocketClass` returns: the WHATWG `WebSocket` surface. */
|
|
25
|
+
export interface PortWebSocket extends EventTarget {
|
|
26
|
+
readonly url: string;
|
|
27
|
+
readonly protocol: string;
|
|
28
|
+
readonly extensions: string;
|
|
29
|
+
readonly readyState: number;
|
|
30
|
+
readonly bufferedAmount: number;
|
|
31
|
+
binaryType: BinaryType;
|
|
32
|
+
onopen: Listener;
|
|
33
|
+
onmessage: ((event: MessageEvent) => void) | null;
|
|
34
|
+
onerror: Listener;
|
|
35
|
+
onclose: ((event: CloseEvent) => void) | null;
|
|
36
|
+
send(data: string | ArrayBufferLike | ArrayBufferView | Blob): void;
|
|
37
|
+
close(code?: number, reason?: string): void;
|
|
38
|
+
/** Not in the standard: see `PortSocketCore.whenDrained`. */
|
|
39
|
+
whenDrained(limit?: number): Promise<void>;
|
|
40
|
+
readonly CONNECTING: 0;
|
|
41
|
+
readonly OPEN: 1;
|
|
42
|
+
readonly CLOSING: 2;
|
|
43
|
+
readonly CLOSED: 3;
|
|
44
|
+
}
|
|
45
|
+
export interface PortWebSocketClass {
|
|
46
|
+
new (url: string | URL, protocols?: string | string[]): PortWebSocket;
|
|
47
|
+
readonly CONNECTING: 0;
|
|
48
|
+
readonly OPEN: 1;
|
|
49
|
+
readonly CLOSING: 2;
|
|
50
|
+
readonly CLOSED: 3;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* A WHATWG-compatible `WebSocket` class whose transport is a `MessagePort`
|
|
54
|
+
* from `connect`. Code written against the browser's `WebSocket` runs on it
|
|
55
|
+
* unchanged:
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* const WS = createWebSocketClass(adapter.connectWebSocket);
|
|
59
|
+
* const ws = new WS(`${baseUrl}chat`, ["v1"]);
|
|
60
|
+
* ws.onmessage = (event) => console.log(event.data);
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* It validates as the standard does (url, scheme, fragment, subprotocol
|
|
64
|
+
* tokens and duplicates, close codes and reasons), has `binaryType`, the `on*`
|
|
65
|
+
* handlers next to `addEventListener`, and the close handshake.
|
|
66
|
+
* `bufferedAmount` is real: the server acknowledges what it consumed.
|
|
67
|
+
*
|
|
68
|
+
* The real `globalThis.WebSocket` is left alone; install the class where you
|
|
69
|
+
* want it.
|
|
70
|
+
*/
|
|
71
|
+
export declare function createWebSocketClass(connect: WebSocketConnector, options?: WebSocketClassOptions): PortWebSocketClass;
|
|
72
|
+
export {};
|
|
73
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/ws/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAa9E,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,WAAW,CAAC;IAClB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;AAEpG,MAAM,WAAW,qBAAqB;IACpC,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,QAAQ,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;AAEhD,qGAAqG;AACrG,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,QAAQ,CAAC;IACjB,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAClD,OAAO,EAAE,QAAQ,CAAC;IAClB,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC9C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;IACpE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,6DAA6D;IAC7D,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,aAAa,CAAC;IACtE,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;CACpB;AAKD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,GAAE,qBAA0B,GAClC,kBAAkB,CAsNpB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The wire protocol of a WebSocket carried over a `MessagePort`.
|
|
3
|
+
*
|
|
4
|
+
* A `MessagePort` is a natural carrier for a WebSocket: messages arrive in
|
|
5
|
+
* order, and strings and transferable `ArrayBuffer`s cross it as they are. The
|
|
6
|
+
* protocol on top is these frames, the same in both directions:
|
|
7
|
+
*
|
|
8
|
+
* - `m` — one message. An `ArrayBuffer` is transferred, never copied.
|
|
9
|
+
* - `c` — close. The side that closes first sends it; the other answers with
|
|
10
|
+
* its own `c` (the echo), and both are then closed cleanly.
|
|
11
|
+
* - `p` — ping, client to server, carrying the client's heartbeat period.
|
|
12
|
+
* - `q` — pong, server to client.
|
|
13
|
+
* - `a` — ack: the receiver consumed `n` more bytes of messages. This is the
|
|
14
|
+
* flow control: what a side has sent and not yet seen acknowledged is its
|
|
15
|
+
* `bufferedAmount`, and the server end can wait for it to fall
|
|
16
|
+
* (`whenDrained`). Added in version 2; version 1 had no backpressure.
|
|
17
|
+
*
|
|
18
|
+
* HEARTBEAT. A `MessagePort` never reports that the other side died, so the
|
|
19
|
+
* client pings every `hb` ms and closes with 1006 when pongs stop for `2·hb`;
|
|
20
|
+
* the server closes with 1006 when pings stop for `3·hb`. That is how a socket
|
|
21
|
+
* notices a closed tab or a terminated worker.
|
|
22
|
+
*
|
|
23
|
+
* Unknown frames are ignored, so a later version can add frames a peer of
|
|
24
|
+
* this version skips.
|
|
25
|
+
*/
|
|
26
|
+
/** The version of this frame protocol. Sent in the upgrade hand-shake. */
|
|
27
|
+
export declare const WS_PORT_PROTOCOL_VERSION = 2;
|
|
28
|
+
/** Default heartbeat period of the client, in ms. */
|
|
29
|
+
export declare const DEFAULT_HEARTBEAT_MS = 10000;
|
|
30
|
+
/** How long a side waits for the echo of its close frame, in ms. */
|
|
31
|
+
export declare const DEFAULT_CLOSE_TIMEOUT_MS = 5000;
|
|
32
|
+
export type WsFrame = {
|
|
33
|
+
t: "m";
|
|
34
|
+
d: string | ArrayBuffer;
|
|
35
|
+
} | {
|
|
36
|
+
t: "c";
|
|
37
|
+
code: number;
|
|
38
|
+
reason: string;
|
|
39
|
+
} | {
|
|
40
|
+
t: "p";
|
|
41
|
+
hb: number;
|
|
42
|
+
} | {
|
|
43
|
+
t: "q";
|
|
44
|
+
} | {
|
|
45
|
+
t: "a";
|
|
46
|
+
n: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The timer functions a socket uses. On a page's main thread, background
|
|
50
|
+
* throttling delays timers by up to a minute, which would close healthy
|
|
51
|
+
* sockets; pass unthrottled ones (worker-timers, say) there.
|
|
52
|
+
*/
|
|
53
|
+
export interface WsTimers {
|
|
54
|
+
setTimeout(fn: () => void, ms: number): unknown;
|
|
55
|
+
clearTimeout(id: unknown): void;
|
|
56
|
+
setInterval(fn: () => void, ms: number): unknown;
|
|
57
|
+
clearInterval(id: unknown): void;
|
|
58
|
+
}
|
|
59
|
+
export declare const defaultTimers: WsTimers;
|
|
60
|
+
/** Recovers a frame from a message's data, or `undefined` for anything else. */
|
|
61
|
+
export declare function readFrame(data: unknown): WsFrame | undefined;
|
|
62
|
+
/** Posts `frame`, transferring a message's `ArrayBuffer`. */
|
|
63
|
+
export declare function postFrame(port: MessagePort, frame: WsFrame): void;
|
|
64
|
+
/** The size a message counts for in `bufferedAmount`: UTF-8 bytes, or the buffer's length. */
|
|
65
|
+
export declare function messageSize(data: string | ArrayBuffer): number;
|
|
66
|
+
/** The UTF-8 length of `text`, without encoding it. */
|
|
67
|
+
export declare function utf8Length(text: string): number;
|
|
68
|
+
//# sourceMappingURL=frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/ws/frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,qDAAqD;AACrD,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C,oEAAoE;AACpE,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,MAAM,MAAM,OAAO,GACf;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,GAAG,WAAW,CAAA;CAAE,GACnC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACtB;IAAE,CAAC,EAAE,GAAG,CAAA;CAAE,GACV;IAAE,CAAC,EAAE,GAAG,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1B;;;;GAIG;AACH,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAChD,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjD,aAAa,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,QAK3B,CAAC;AAEF,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CA2B5D;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAGjE;AAED,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,MAAM,CAE9D;AAED,uDAAuD;AACvD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAe/C"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./client.js";
|
|
2
|
+
export { DEFAULT_CLOSE_TIMEOUT_MS, DEFAULT_HEARTBEAT_MS, WS_PORT_PROTOCOL_VERSION, type WsFrame, type WsTimers, } from "./frames.js";
|
|
3
|
+
export * from "./server.js";
|
|
4
|
+
export { WsCloseEvent, type WsCloseEventInit } from "./socket-core.js";
|
|
5
|
+
export * from "./upgrade.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ws/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,wBAAwB,EACxB,KAAK,OAAO,EACZ,KAAK,QAAQ,GACd,MAAM,aAAa,CAAC;AACrB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACvE,cAAc,cAAc,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type WsFrame, type WsTimers } from "./frames.js";
|
|
2
|
+
import { PortSocketCore } from "./socket-core.js";
|
|
3
|
+
export interface ServerWebSocketOptions {
|
|
4
|
+
/** The url the client asked for. */
|
|
5
|
+
url?: string;
|
|
6
|
+
/** The subprotocol the server picked, `""` for none. */
|
|
7
|
+
protocol?: string;
|
|
8
|
+
timers?: WsTimers;
|
|
9
|
+
/** How long `close()` waits for the client's echo, in ms. Default 5 s. */
|
|
10
|
+
closeTimeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The server end of a socket over a `MessagePort`: what a handler gets from
|
|
14
|
+
* `upgradeWebSocket` and talks to.
|
|
15
|
+
*
|
|
16
|
+
* - Events are delivered only after `accept()`; frames that arrive earlier are
|
|
17
|
+
* queued. Pings are answered even before `accept()`, so a slow handler does
|
|
18
|
+
* not look like a dead one.
|
|
19
|
+
* - Messages arrive as strings or `ArrayBuffer`s.
|
|
20
|
+
* - `bufferedAmount` counts what the client has not consumed yet, and
|
|
21
|
+
* `whenDrained(limit)` waits for it to fall: the backpressure a producer
|
|
22
|
+
* applies.
|
|
23
|
+
* - When the client's pings stop for three heartbeat periods (a closed tab, a
|
|
24
|
+
* terminated worker), the socket closes with 1006.
|
|
25
|
+
*/
|
|
26
|
+
export declare class ServerWebSocket extends PortSocketCore {
|
|
27
|
+
static readonly CONNECTING = 0;
|
|
28
|
+
static readonly OPEN = 1;
|
|
29
|
+
static readonly CLOSING = 2;
|
|
30
|
+
static readonly CLOSED = 3;
|
|
31
|
+
readonly url: string;
|
|
32
|
+
readonly protocol: string;
|
|
33
|
+
private _accepted;
|
|
34
|
+
private _queue;
|
|
35
|
+
private _heartbeatMs;
|
|
36
|
+
private _lastPing;
|
|
37
|
+
private _watch;
|
|
38
|
+
constructor(port: MessagePort, options?: ServerWebSocketOptions);
|
|
39
|
+
/** Whether `accept()` was called. */
|
|
40
|
+
get accepted(): boolean;
|
|
41
|
+
/** Starts delivering events, the queued ones first. */
|
|
42
|
+
accept(): void;
|
|
43
|
+
send(data: string | ArrayBuffer | ArrayBufferView): void;
|
|
44
|
+
close(code?: number, reason?: string): void;
|
|
45
|
+
protected _deliver(frame: Extract<WsFrame, {
|
|
46
|
+
t: "m" | "c";
|
|
47
|
+
}>): void;
|
|
48
|
+
protected _dispatchMessage(data: string | ArrayBuffer): void;
|
|
49
|
+
protected _onPing(hb: number): void;
|
|
50
|
+
protected _onClosed(): void;
|
|
51
|
+
private _watchHeartbeat;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/ws/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EAML,cAAc,EAEf,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,sBAAsB;IACrC,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAC;IAClB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,SAAQ,cAAc;IACjD,MAAM,CAAC,QAAQ,CAAC,UAAU,KAAc;IACxC,MAAM,CAAC,QAAQ,CAAC,IAAI,KAAQ;IAC5B,MAAM,CAAC,QAAQ,CAAC,OAAO,KAAW;IAClC,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAU;IAEhC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAiD;IAC/D,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,MAAM,CAAU;IAExB,YAAY,IAAI,EAAE,WAAW,EAAE,OAAO,GAAE,sBAA2B,EAOlE;IAED,qCAAqC;IACrC,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,uDAAuD;IACvD,MAAM,IAAI,IAAI,CAIb;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAAG,IAAI,CAKvD;IAED,KAAK,CAAC,IAAI,SAAO,EAAE,MAAM,SAAK,GAAG,IAAI,CAWpC;IAED,UAAmB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,EAAE;QAAE,CAAC,EAAE,GAAG,GAAG,GAAG,CAAA;KAAE,CAAC,GAAG,IAAI,CAM3E;IAED,SAAS,CAAC,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAE3D;IAED,UAAmB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAO3C;IAED,UAAmB,SAAS,IAAI,IAAI,CAGnC;IAED,OAAO,CAAC,eAAe;CAQxB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type WsFrame, type WsTimers } from "./frames.js";
|
|
2
|
+
export declare const CONNECTING = 0;
|
|
3
|
+
export declare const OPEN = 1;
|
|
4
|
+
export declare const CLOSING = 2;
|
|
5
|
+
export declare const CLOSED = 3;
|
|
6
|
+
export interface WsCloseEventInit extends EventInit {
|
|
7
|
+
code?: number;
|
|
8
|
+
reason?: string;
|
|
9
|
+
wasClean?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* A `CloseEvent` for runtimes that have none: Node 22 has `Event` and
|
|
13
|
+
* `MessageEvent` as globals, but not `CloseEvent`.
|
|
14
|
+
*/
|
|
15
|
+
export declare class WsCloseEvent extends Event {
|
|
16
|
+
readonly code: number;
|
|
17
|
+
readonly reason: string;
|
|
18
|
+
readonly wasClean: boolean;
|
|
19
|
+
constructor(type: string, init?: WsCloseEventInit);
|
|
20
|
+
}
|
|
21
|
+
/** A `close` event: the platform's `CloseEvent` where there is one. */
|
|
22
|
+
export declare function newCloseEvent(init: WsCloseEventInit): Event;
|
|
23
|
+
export interface PortSocketOptions {
|
|
24
|
+
timers?: WsTimers;
|
|
25
|
+
/** How long to wait for the echo of a close frame before giving up with 1006. */
|
|
26
|
+
closeTimeoutMs?: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* What both ends of a socket over a `MessagePort` share: the frames, the close
|
|
30
|
+
* handshake, and the byte accounting behind `bufferedAmount`.
|
|
31
|
+
*
|
|
32
|
+
* Not exported from the package: the two ends are `createWebSocketClass`'s
|
|
33
|
+
* class and `ServerWebSocket`.
|
|
34
|
+
*/
|
|
35
|
+
export declare abstract class PortSocketCore extends EventTarget {
|
|
36
|
+
protected _port?: MessagePort;
|
|
37
|
+
protected _state: number;
|
|
38
|
+
protected readonly _timers: WsTimers;
|
|
39
|
+
protected readonly _closeTimeoutMs: number;
|
|
40
|
+
/** Bytes sent and not yet acknowledged by the peer, plus those still being prepared. */
|
|
41
|
+
private _inFlight;
|
|
42
|
+
/** Bytes of messages "sent" after the socket started closing: counted, never sent. */
|
|
43
|
+
private _discarded;
|
|
44
|
+
private _toAck;
|
|
45
|
+
private _ackScheduled;
|
|
46
|
+
private _closeTimer;
|
|
47
|
+
private _drainWaiters;
|
|
48
|
+
constructor(options?: PortSocketOptions);
|
|
49
|
+
get readyState(): number;
|
|
50
|
+
/**
|
|
51
|
+
* Bytes queued by `send()` that the peer has not consumed yet. A peer
|
|
52
|
+
* acknowledges what it has dispatched, so this falls as the other side reads.
|
|
53
|
+
*/
|
|
54
|
+
get bufferedAmount(): number;
|
|
55
|
+
/**
|
|
56
|
+
* Resolves once `bufferedAmount` is at most `limit` (default 0), or the
|
|
57
|
+
* socket is closed. The backpressure a producer applies:
|
|
58
|
+
* `if (socket.bufferedAmount > HIGH) await socket.whenDrained(LOW)`.
|
|
59
|
+
*/
|
|
60
|
+
whenDrained(limit?: number): Promise<void>;
|
|
61
|
+
protected _attach(port: MessagePort): void;
|
|
62
|
+
protected _onFrame(frame: WsFrame): void;
|
|
63
|
+
/** A message or close frame the socket is ready to act on. */
|
|
64
|
+
protected _deliver(frame: Extract<WsFrame, {
|
|
65
|
+
t: "m" | "c";
|
|
66
|
+
}>): void;
|
|
67
|
+
protected abstract _dispatchMessage(data: string | ArrayBuffer): void;
|
|
68
|
+
protected _onPing(_hb: number): void;
|
|
69
|
+
protected _onPong(): void;
|
|
70
|
+
protected _onClosed(): void;
|
|
71
|
+
/** Acknowledge consumed bytes, coalescing the acks of one turn into one frame. */
|
|
72
|
+
protected _consumed(bytes: number): void;
|
|
73
|
+
/** Reserves `bytes` in `bufferedAmount` for a message still being prepared (a Blob). */
|
|
74
|
+
protected _reserve(bytes: number): void;
|
|
75
|
+
/** Sends one message whose bytes were reserved with `_reserve` (or `reserved = false`). */
|
|
76
|
+
protected _sendData(data: string | ArrayBuffer, reserved?: boolean): void;
|
|
77
|
+
protected _post(frame: WsFrame): void;
|
|
78
|
+
/** Starts the close handshake. */
|
|
79
|
+
protected _startClose(code: number, reason: string): void;
|
|
80
|
+
protected _onCloseFrame(code: number, reason: string): void;
|
|
81
|
+
/** The socket is closed: release the port and tell the listeners. */
|
|
82
|
+
protected _finish(code: number, reason: string, wasClean: boolean, error?: boolean): void;
|
|
83
|
+
private _wakeDrainWaiters;
|
|
84
|
+
}
|
|
85
|
+
/** Copies `data` into a fresh `ArrayBuffer`: `send()` must never detach the caller's buffer. */
|
|
86
|
+
export declare function toArrayBuffer(data: ArrayBuffer | ArrayBufferView): ArrayBuffer;
|
|
87
|
+
export declare function isBinary(data: unknown): data is ArrayBuffer | ArrayBufferView;
|
|
88
|
+
export declare function isBlob(data: unknown): data is Blob;
|
|
89
|
+
/** Valid close codes for an application to send: 1000, or 3000–4999. */
|
|
90
|
+
export declare function isApplicationCloseCode(code: number): boolean;
|
|
91
|
+
//# sourceMappingURL=socket-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"socket-core.d.ts","sourceRoot":"","sources":["../../src/ws/socket-core.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { ServerWebSocket, type ServerWebSocketOptions } from "./server.js";
|
|
2
|
+
/**
|
|
3
|
+
* The upgrade, as an HTTP exchange: a `GET` with `Upgrade: websocket` goes to
|
|
4
|
+
* an ordinary `(Request) => Response` handler, and a `101` answer carrying a
|
|
5
|
+
* server socket means accepted. Anything else is a refusal, with its status.
|
|
6
|
+
*
|
|
7
|
+
* FORBIDDEN HEADERS. A browser drops `Upgrade`, `Connection` and every
|
|
8
|
+
* `Sec-*` header from a `new Request()`. The upgrade request therefore carries
|
|
9
|
+
* its headers as an own `headers` property, a standalone `Headers` that keeps
|
|
10
|
+
* them, so handler code reads `request.headers.get("upgrade")` as usual.
|
|
11
|
+
*
|
|
12
|
+
* NO 101 IN A `Response`. The constructor refuses statuses below 200, so the
|
|
13
|
+
* response `upgradeWebSocket` returns reports 101 through an own `status`
|
|
14
|
+
* property; the port that goes back to the caller is found with `takePort`.
|
|
15
|
+
*/
|
|
16
|
+
/** The request header carrying the client's version of the frame protocol. */
|
|
17
|
+
export declare const WS_VERSION_HEADER = "x-webrun-ws-version";
|
|
18
|
+
export interface WebSocketUpgradeInit {
|
|
19
|
+
/** Requested subprotocols, in order of preference. */
|
|
20
|
+
protocols?: string[];
|
|
21
|
+
/** More headers for the handler to see (cookies, origin, authorization...). */
|
|
22
|
+
headers?: HeadersInit;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The `GET` a handler sees for `new WebSocket(url, protocols)`. A `ws:` or
|
|
26
|
+
* `wss:` url is given as `http:` or `https:`, as a server sees it.
|
|
27
|
+
*/
|
|
28
|
+
export declare function newWebSocketUpgradeRequest(url: string | URL, { protocols, headers }?: WebSocketUpgradeInit): Request;
|
|
29
|
+
/**
|
|
30
|
+
* Re-attaches `headers` to `request` as its own `headers` property, keeping
|
|
31
|
+
* the forbidden ones a browser dropped. Returns `request`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function withHeaders(request: Request, headers: Headers): Request;
|
|
34
|
+
/** Whether `request` asks for a WebSocket. */
|
|
35
|
+
export declare function isWebSocketUpgrade(request: Request): boolean;
|
|
36
|
+
/** The subprotocols `request` asks for, in order of preference. */
|
|
37
|
+
export declare function requestedProtocols(request: Request): string[];
|
|
38
|
+
export interface UpgradeWebSocketOptions extends Omit<ServerWebSocketOptions, "url"> {
|
|
39
|
+
}
|
|
40
|
+
export interface WebSocketUpgrade {
|
|
41
|
+
/** The server end. Call `accept()`, then listen and send. */
|
|
42
|
+
socket: ServerWebSocket;
|
|
43
|
+
/** Return it from the handler: status 101. */
|
|
44
|
+
response: Response;
|
|
45
|
+
}
|
|
46
|
+
interface PendingUpgrade {
|
|
47
|
+
port: MessagePort;
|
|
48
|
+
protocol: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Accepts a WebSocket upgrade in a handler:
|
|
52
|
+
*
|
|
53
|
+
* ```ts
|
|
54
|
+
* if (isWebSocketUpgrade(request)) {
|
|
55
|
+
* const { socket, response } = upgradeWebSocket(request, { protocol: "v1" });
|
|
56
|
+
* socket.accept();
|
|
57
|
+
* socket.addEventListener("message", (event) => socket.send(event.data));
|
|
58
|
+
* return response;
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* The host takes the client's port from the response with `takePort` and
|
|
63
|
+
* hands it to the caller.
|
|
64
|
+
*/
|
|
65
|
+
export declare function upgradeWebSocket(request: Request, options?: UpgradeWebSocketOptions): WebSocketUpgrade;
|
|
66
|
+
/**
|
|
67
|
+
* The port to hand to the caller, for a response from `upgradeWebSocket`.
|
|
68
|
+
* Taken once: a second call returns `undefined`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function takePort(response: Response): MessagePort | undefined;
|
|
71
|
+
/** `takePort`, with the subprotocol the server picked. */
|
|
72
|
+
export declare function takeUpgrade(response: Response): PendingUpgrade | undefined;
|
|
73
|
+
export type WebSocketUpgradeResult = {
|
|
74
|
+
ok: true;
|
|
75
|
+
port: MessagePort;
|
|
76
|
+
protocol: string;
|
|
77
|
+
} | {
|
|
78
|
+
ok: false;
|
|
79
|
+
status: number;
|
|
80
|
+
message: string;
|
|
81
|
+
};
|
|
82
|
+
export interface ServeWebSocketUpgradeInit extends WebSocketUpgradeInit {
|
|
83
|
+
url: string;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Runs an upgrade through `handler`, as a host does when a caller asks for a
|
|
87
|
+
* socket: builds the upgrade request, calls the handler, and either takes the
|
|
88
|
+
* port of a `101` or reports the refusal. A subprotocol the caller did not ask
|
|
89
|
+
* for is a refusal, as in the standard.
|
|
90
|
+
*/
|
|
91
|
+
export declare function serveWebSocketUpgrade(handler: (request: Request) => Response | Promise<Response>, { url, protocols, headers }: ServeWebSocketUpgradeInit): Promise<WebSocketUpgradeResult>;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=upgrade.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/ws/upgrade.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAE3E;;;;;;;;;;;;;GAaG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AAEvD,MAAM,WAAW,oBAAoB;IACnC,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,+EAA+E;IAC/E,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,EAAE,SAAc,EAAE,OAAO,EAAE,GAAE,oBAAyB,GACrD,OAAO,CAYT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAGvE;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,mEAAmE;AACnE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,CAO7D;AAED,MAAM,WAAW,uBAAwB,SAAQ,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC;CAAG;AAEvF,MAAM,WAAW,gBAAgB;IAC/B,6DAA6D;IAC7D,MAAM,EAAE,eAAe,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,uBAA4B,GACpC,gBAAgB,CAelB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,WAAW,GAAG,SAAS,CAEpE;AAED,0DAA0D;AAC1D,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,cAAc,GAAG,SAAS,CAK1E;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,yBAA0B,SAAQ,oBAAoB;IACrE,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,EAC3D,EAAE,GAAG,EAAE,SAAc,EAAE,OAAO,EAAE,EAAE,yBAAyB,GAC1D,OAAO,CAAC,sBAAsB,CAAC,CA4BjC"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-http-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"description": "ServiceWorker-based HTTP server for browsers
|
|
6
|
+
"description": "ServiceWorker-based HTTP and WebSocket server for browsers: handlers in pages and Dedicated Workers, same-origin and relay modes, streams with backpressure",
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/statewalker/webrun-wire/issues"
|
|
9
9
|
},
|
|
@@ -40,6 +40,11 @@
|
|
|
40
40
|
"types": "./dist/relay-worker.d.ts",
|
|
41
41
|
"import": "./dist/relay-worker.js"
|
|
42
42
|
},
|
|
43
|
+
"./worker": {
|
|
44
|
+
"source": "./src/worker.ts",
|
|
45
|
+
"types": "./dist/worker.d.ts",
|
|
46
|
+
"import": "./dist/worker.js"
|
|
47
|
+
},
|
|
43
48
|
"./sw-worker": {
|
|
44
49
|
"default": "./dist/sw-worker.js"
|
|
45
50
|
}
|
|
@@ -53,9 +58,9 @@
|
|
|
53
58
|
],
|
|
54
59
|
"dependencies": {
|
|
55
60
|
"idb-keyval": "^6.3.0",
|
|
56
|
-
"@statewalker/webrun-http-streams": "^0.
|
|
57
|
-
"@statewalker/webrun-
|
|
58
|
-
"@statewalker/webrun-
|
|
61
|
+
"@statewalker/webrun-http-streams": "^0.3.0",
|
|
62
|
+
"@statewalker/webrun-rpc": "^0.5.0",
|
|
63
|
+
"@statewalker/webrun-streams": "^0.2.2"
|
|
59
64
|
},
|
|
60
65
|
"devDependencies": {
|
|
61
66
|
"@biomejs/biome": "^2.5.15",
|
package/src/core/data-calls.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deserializeError, serializeError } from "@statewalker/webrun-streams";
|
|
2
|
+
import { withDeadline } from "./deadline.js";
|
|
2
3
|
import type { MessageSink, MessageSource } from "./message-target.js";
|
|
3
4
|
|
|
4
5
|
export type ChannelCallHandler = (
|
|
@@ -7,6 +8,34 @@ export type ChannelCallHandler = (
|
|
|
7
8
|
...transfers: MessagePort[]
|
|
8
9
|
) => unknown;
|
|
9
10
|
|
|
11
|
+
const TRANSFER = Symbol.for("webrun-http-browser:transfer");
|
|
12
|
+
|
|
13
|
+
interface TransferResult {
|
|
14
|
+
[TRANSFER]: true;
|
|
15
|
+
value: unknown;
|
|
16
|
+
transfer: Transferable[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A channel call's result that carries transferables, a `MessagePort` say:
|
|
21
|
+
* return `withTransfer({ port }, [port])` from a `handleChannelCalls` handler
|
|
22
|
+
* and `callChannel` resolves with `{ port }`.
|
|
23
|
+
*/
|
|
24
|
+
export function withTransfer<T>(value: T, transfer: Transferable[]): T {
|
|
25
|
+
return { [TRANSFER]: true, value, transfer } as unknown as T;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isTransferResult(value: unknown): value is TransferResult {
|
|
29
|
+
return typeof value === "object" && value !== null && TRANSFER in value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Splits a handler's result into the value to post and its transfer list. */
|
|
33
|
+
export function unwrapTransfer(result: unknown): { value: unknown; transfer: Transferable[] } {
|
|
34
|
+
return isTransferResult(result)
|
|
35
|
+
? { value: result.value, transfer: result.transfer }
|
|
36
|
+
: { value: result, transfer: [] };
|
|
37
|
+
}
|
|
38
|
+
|
|
10
39
|
export function callChannel<T = unknown>(
|
|
11
40
|
target: MessageSink,
|
|
12
41
|
callType: string,
|
|
@@ -33,14 +62,35 @@ export function handleChannelCalls(
|
|
|
33
62
|
if (!event.data || event.data.type !== callType) return;
|
|
34
63
|
const [port, ...transfers] = event.ports ?? [];
|
|
35
64
|
const response: { result?: unknown; error?: unknown } = {};
|
|
65
|
+
let transfer: Transferable[] = [];
|
|
36
66
|
try {
|
|
37
|
-
|
|
67
|
+
const result = await handler(event, event.data.params, ...transfers);
|
|
68
|
+
({ value: response.result, transfer } = unwrapTransfer(result));
|
|
38
69
|
} catch (error) {
|
|
39
70
|
response.error = serializeError(error);
|
|
40
71
|
}
|
|
41
|
-
port?.postMessage(response);
|
|
72
|
+
port?.postMessage(response, transfer);
|
|
42
73
|
};
|
|
43
74
|
target.addEventListener("message", listener);
|
|
44
75
|
target.start?.();
|
|
45
76
|
return () => target.removeEventListener("message", listener);
|
|
46
77
|
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* `callChannel`, failing after `ms` instead of waiting for ever: a peer that
|
|
81
|
+
* died (a ServiceWorker stopped mid-call) or that does not know `callType` (an
|
|
82
|
+
* older version) never answers.
|
|
83
|
+
*/
|
|
84
|
+
export function callChannelWithin<T = unknown>(
|
|
85
|
+
ms: number,
|
|
86
|
+
target: MessageSink,
|
|
87
|
+
callType: string,
|
|
88
|
+
params: unknown,
|
|
89
|
+
...transfers: Transferable[]
|
|
90
|
+
): Promise<T> {
|
|
91
|
+
return withDeadline(
|
|
92
|
+
Date.now() + ms,
|
|
93
|
+
callChannel<T>(target, callType, params, ...transfers),
|
|
94
|
+
() => new Error(`No answer to ${callType} within ${ms} ms`),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
@@ -26,8 +26,14 @@ export const CLAIM_CALL = "CLAIM";
|
|
|
26
26
|
* - `uncontrolled`: the worker is active but the page is not controlled by it.
|
|
27
27
|
* - `unresponsive`: the page is controlled, but the worker did not answer the
|
|
28
28
|
* adapter's handshake in time.
|
|
29
|
+
* - `incompatible`: the worker answered, but runs another version of this
|
|
30
|
+
* package's link protocol.
|
|
29
31
|
*/
|
|
30
|
-
export type ServiceWorkerControlFailure =
|
|
32
|
+
export type ServiceWorkerControlFailure =
|
|
33
|
+
| "activation-timeout"
|
|
34
|
+
| "uncontrolled"
|
|
35
|
+
| "unresponsive"
|
|
36
|
+
| "incompatible";
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* Why a page could not get a working ServiceWorker. `reason` says which wait
|
package/src/index.ts
CHANGED