@kyneta/websocket-transport 1.3.1 → 1.5.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.
@@ -0,0 +1,134 @@
1
+ import { c as WebSocketConstructor, d as WebsocketClientState, f as WebsocketClientStateTransition, t as DisconnectReason } from "./types-c1S_xIRG.js";
2
+ import { GeneratedChannel, PeerId, Transport, TransportFactory } from "@kyneta/transport";
3
+ import { TransitionListener as TransitionListener$1 } from "@kyneta/machine";
4
+
5
+ //#region src/client-transport.d.ts
6
+ /**
7
+ * Default fragment threshold in bytes.
8
+ * AWS API Gateway has a 128KB limit, so 100KB provides a safe margin.
9
+ */
10
+ declare const DEFAULT_FRAGMENT_THRESHOLD: number;
11
+ /**
12
+ * Options for the Websocket client transport (browser connections).
13
+ */
14
+ interface WebsocketClientOptions {
15
+ /** Websocket URL to connect to. Can be a string or a function of peerId. */
16
+ url: string | ((peerId: PeerId) => string);
17
+ /**
18
+ * WebSocket constructor — caller must provide explicitly.
19
+ *
20
+ * In browsers, pass the global `WebSocket`. In Node.js, pass `ws`'s
21
+ * `WebSocket`. In Bun, pass `globalThis.WebSocket`. The transport
22
+ * never probes `globalThis` on its own.
23
+ */
24
+ WebSocket: WebSocketConstructor;
25
+ /**
26
+ * Headers to send during Websocket upgrade.
27
+ * Used for authentication in service-to-service communication.
28
+ *
29
+ * Note: Headers are a Bun/Node-specific extension. The browser WebSocket
30
+ * API does not support custom headers per the WHATWG spec.
31
+ */
32
+ headers?: Record<string, string>;
33
+ /** Reconnection options. */
34
+ reconnect?: {
35
+ enabled?: boolean;
36
+ maxAttempts?: number;
37
+ baseDelay?: number;
38
+ maxDelay?: number;
39
+ };
40
+ /** Keepalive interval in ms (default: 30000). */
41
+ keepaliveInterval?: number;
42
+ /**
43
+ * Fragment threshold in bytes. Messages larger than this are fragmented.
44
+ * Set to 0 to disable fragmentation (not recommended for cloud deployments).
45
+ * Default: 100KB
46
+ */
47
+ fragmentThreshold?: number;
48
+ /** Lifecycle event callbacks. */
49
+ lifecycle?: WebsocketClientLifecycleEvents;
50
+ }
51
+ /**
52
+ * Lifecycle event callbacks for the Websocket client.
53
+ */
54
+ interface WebsocketClientLifecycleEvents {
55
+ /** Called on every state transition (delivered async via microtask). */
56
+ onStateChange?: (transition: WebsocketClientStateTransition) => void;
57
+ /** Called when the connection is lost. */
58
+ onDisconnect?: (reason: DisconnectReason) => void;
59
+ /** Called when a reconnection attempt is scheduled. */
60
+ onReconnecting?: (attempt: number, nextAttemptMs: number) => void;
61
+ /** Called when reconnection succeeds after a previous connection. */
62
+ onReconnected?: () => void;
63
+ /** Called when the server sends the "ready" signal. */
64
+ onReady?: () => void;
65
+ }
66
+ /**
67
+ * Websocket client network transport for @kyneta/exchange.
68
+ *
69
+ * Connects to a Websocket server, sends and receives ChannelMsg via
70
+ * the kyneta wire format (CBOR codec + framing + fragmentation).
71
+ *
72
+ * Internally, the connection lifecycle is a `Program<Msg, Model, Fx>` —
73
+ * a pure Mealy machine whose transitions are deterministically testable.
74
+ * This class is the imperative shell that interprets data effects as I/O.
75
+ *
76
+ * Prefer the factory functions for construction:
77
+ * - `createWebsocketClient()` — browser-to-server (from `./browser`)
78
+ * - `createServiceWebsocketClient()` — service-to-service with headers (from `./server`)
79
+ */
80
+ declare class WebsocketClientTransport extends Transport<void> {
81
+ #private;
82
+ constructor(options: WebsocketClientOptions);
83
+ /**
84
+ * Get the current connection state.
85
+ */
86
+ getState(): WebsocketClientState;
87
+ /**
88
+ * Subscribe to state transitions.
89
+ */
90
+ subscribeToTransitions(listener: TransitionListener$1<WebsocketClientState>): () => void;
91
+ /**
92
+ * Wait for a specific state.
93
+ */
94
+ waitForState(predicate: (state: WebsocketClientState) => boolean, options?: {
95
+ timeoutMs?: number;
96
+ }): Promise<WebsocketClientState>;
97
+ /**
98
+ * Wait for a specific status.
99
+ */
100
+ waitForStatus(status: WebsocketClientState["status"], options?: {
101
+ timeoutMs?: number;
102
+ }): Promise<WebsocketClientState>;
103
+ /**
104
+ * Whether the client is ready (server ready signal received).
105
+ */
106
+ get isReady(): boolean;
107
+ protected generate(): GeneratedChannel;
108
+ onStart(): Promise<void>;
109
+ onStop(): Promise<void>;
110
+ }
111
+ /**
112
+ * Create a Websocket client transport factory for browser-to-server
113
+ * connections.
114
+ *
115
+ * Returns an `TransportFactory` — a closure that creates a fresh transport
116
+ * instance when called. Pass directly to `Exchange({ transports: [...] })`.
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * import { createWebsocketClient } from "@kyneta/websocket-transport/browser"
121
+ *
122
+ * const exchange = new Exchange({
123
+ * transports: [createWebsocketClient({
124
+ * url: "ws://localhost:3000/ws",
125
+ * WebSocket,
126
+ * reconnect: { enabled: true },
127
+ * })],
128
+ * })
129
+ * ```
130
+ */
131
+ declare function createWebsocketClient(options: WebsocketClientOptions): TransportFactory;
132
+ //#endregion
133
+ export { createWebsocketClient as a, WebsocketClientTransport as i, WebsocketClientLifecycleEvents as n, WebsocketClientOptions as r, DEFAULT_FRAGMENT_THRESHOLD as t };
134
+ //# sourceMappingURL=client-transport-D3tYQYrS.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-transport-D3tYQYrS.d.ts","names":[],"sources":["../src/client-transport.ts"],"mappings":";;;;;;;;;cAkEa,0BAAA;;;;UAKI,sBAAA;EAoBL;EAlBV,GAAA,aAAgB,MAAA,EAAQ,MAAA;EAuCkB;;;;;;;EA9B1C,SAAA,EAAW,oBAAA;EASD;;;;;;;EAAV,OAAA,GAAU,MAAA;EAqBV;EAlBA,SAAA;IACE,OAAA;IACA,WAAA;IACA,SAAA;IACA,QAAA;EAAA;EAyBsC;EArBxC,iBAAA;EAkB6B;;;;;EAX7B,iBAAA;EAiBkB;EAdlB,SAAA,GAAY,8BAAA;AAAA;;;;UAMG,8BAAA;EAmCqB;EAjCpC,aAAA,IAAiB,UAAA,EAAY,8BAAA;EAsDR;EAnDrB,YAAA,IAAgB,MAAA,EAAQ,gBAAA;EA0YO;EAvY/B,cAAA,IAAkB,OAAA,UAAiB,aAAA;EAgZd;EA7YrB,aAAA;EA+YG;EA5YH,OAAA;AAAA;;;;;;;;;;;;;;;cAqBW,wBAAA,SAAiC,SAAA;EAAA;cAqBhC,OAAA,EAAS,sBAAA;EAuVnB;;;EARF,QAAA,CAAA,GAAY,oBAAA;EAiBV;;;EAVF,sBAAA,CACE,QAAA,EAAU,oBAAA,CAAmB,oBAAA;EAWpB;;;EAHX,YAAA,CACE,SAAA,GAAY,KAAA,EAAO,oBAAA,cACnB,OAAA;IAAY,SAAA;EAAA,IACX,OAAA,CAAQ,oBAAA;EAUR;;;EAHH,aAAA,CACE,MAAA,EAAQ,oBAAA,YACR,OAAA;IAAY,SAAA;EAAA,IACX,OAAA,CAAQ,oBAAA;EA6CM;;;EAAA,IAtCb,OAAA,CAAA;EAAA,UAQM,QAAA,CAAA,GAAY,gBAAA;EA8BhB,OAAA,CAAA,GAAW,OAAA;EAUX,MAAA,CAAA,GAAU,OAAA;AAAA;;;;;;;;;;;;;;;;;;;;;iBA8BF,qBAAA,CACd,OAAA,EAAS,sBAAA,GACR,gBAAA"}
package/dist/server.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { PeerId, Channel, ChannelMsg, Transport, GeneratedChannel, TransportFactory } from '@kyneta/transport';
2
- import { S as Socket, g as WebsocketConnectionOptions, h as WebsocketConnectionResult } from './types-D0lbeevu.js';
3
- export { D as DisconnectReason, N as NodeWebsocketLike, R as READY_STATE, a as SocketReadyState, b as WebSocketCloseEvent, c as WebSocketConstructor, d as WebSocketLike, e as WebSocketMessageEvent, i as WebsocketConnectionHandle, w as wrapNodeWebsocket, j as wrapStandardWebsocket } from './types-D0lbeevu.js';
4
- import { a as WebsocketClientOptions } from './client-transport-DUAFjVbh.js';
5
- import '@kyneta/machine';
1
+ import { _ as wrapStandardWebsocket, a as SocketReadyState, c as WebSocketConstructor, g as wrapNodeWebsocket, h as WebsocketConnectionResult, i as Socket, l as WebSocketLike, m as WebsocketConnectionOptions, n as NodeWebsocketLike, p as WebsocketConnectionHandle, r as READY_STATE, s as WebSocketCloseEvent, t as DisconnectReason, u as WebSocketMessageEvent } from "./types-c1S_xIRG.js";
2
+ import { r as WebsocketClientOptions } from "./client-transport-D3tYQYrS.js";
3
+ import { Channel, ChannelMsg, GeneratedChannel, PeerId, Transport, TransportFactory } from "@kyneta/transport";
6
4
 
5
+ //#region src/connection.d.ts
7
6
  /**
8
7
  * Default fragment threshold in bytes.
9
8
  * Messages larger than this are fragmented for cloud infrastructure compatibility.
@@ -14,12 +13,12 @@ declare const DEFAULT_FRAGMENT_THRESHOLD: number;
14
13
  * Configuration for creating a WebsocketConnection.
15
14
  */
16
15
  interface WebsocketConnectionConfig {
17
- /**
18
- * Fragment threshold in bytes. Messages larger than this are fragmented.
19
- * Set to 0 to disable fragmentation (not recommended for cloud deployments).
20
- * Default: 100KB (safe for AWS API Gateway's 128KB limit)
21
- */
22
- fragmentThreshold?: number;
16
+ /**
17
+ * Fragment threshold in bytes. Messages larger than this are fragmented.
18
+ * Set to 0 to disable fragmentation (not recommended for cloud deployments).
19
+ * Default: 100KB (safe for AWS API Gateway's 128KB limit)
20
+ */
21
+ fragmentThreshold?: number;
23
22
  }
24
23
  /**
25
24
  * Represents a single Websocket connection to a peer (server-side).
@@ -31,53 +30,55 @@ interface WebsocketConnectionConfig {
31
30
  * the natural choice for Websocket's binary frame support.
32
31
  */
33
32
  declare class WebsocketConnection {
34
- #private;
35
- readonly peerId: PeerId;
36
- readonly channelId: number;
37
- constructor(peerId: PeerId, channelId: number, socket: Socket, config?: WebsocketConnectionConfig);
38
- /**
39
- * Set the channel reference.
40
- * Called by the adapter when the channel is created.
41
- * @internal
42
- */
43
- _setChannel(channel: Channel): void;
44
- /**
45
- * Start processing messages on this connection.
46
- *
47
- * Sets up the message handler on the socket. Must be called after
48
- * the connection is fully set up (channel assigned, stored in adapter).
49
- */
50
- start(): void;
51
- /**
52
- * Send a ChannelMsg through the Websocket.
53
- *
54
- * Encodes via CBOR codec → frame → fragment if needed → socket.send().
55
- */
56
- send(msg: ChannelMsg): void;
57
- /**
58
- * Send a "ready" signal to the client.
59
- *
60
- * This is a transport-level text message that tells the client the
61
- * server is ready to receive protocol messages. The client creates
62
- * its channel and sends establish-request after receiving this.
63
- */
64
- sendReady(): void;
65
- /**
66
- * Close the connection and clean up resources.
67
- */
68
- close(code?: number, reason?: string): void;
33
+ #private;
34
+ readonly peerId: PeerId;
35
+ readonly channelId: number;
36
+ constructor(peerId: PeerId, channelId: number, socket: Socket, config?: WebsocketConnectionConfig);
37
+ /**
38
+ * Set the channel reference.
39
+ * Called by the adapter when the channel is created.
40
+ * @internal
41
+ */
42
+ _setChannel(channel: Channel): void;
43
+ /**
44
+ * Start processing messages on this connection.
45
+ *
46
+ * Sets up the message handler on the socket. Must be called after
47
+ * the connection is fully set up (channel assigned, stored in adapter).
48
+ */
49
+ start(): void;
50
+ /**
51
+ * Send a ChannelMsg through the Websocket.
52
+ *
53
+ * Pipeline: alias transformer wire encode → frame → fragment if needed
54
+ * → socket.send().
55
+ */
56
+ send(msg: ChannelMsg): void;
57
+ /**
58
+ * Send a "ready" signal to the client.
59
+ *
60
+ * This is a transport-level text message that tells the client the
61
+ * server is ready to receive protocol messages. The client creates
62
+ * its channel and sends establish after receiving this.
63
+ */
64
+ sendReady(): void;
65
+ /**
66
+ * Close the connection and clean up resources.
67
+ */
68
+ close(code?: number, reason?: string): void;
69
69
  }
70
-
70
+ //#endregion
71
+ //#region src/server-transport.d.ts
71
72
  /**
72
73
  * Options for the Websocket server adapter.
73
74
  */
74
75
  interface WebsocketServerTransportOptions {
75
- /**
76
- * Fragment threshold in bytes. Messages larger than this are fragmented.
77
- * Set to 0 to disable fragmentation (not recommended for cloud deployments).
78
- * Default: 100KB (safe for AWS API Gateway's 128KB limit)
79
- */
80
- fragmentThreshold?: number;
76
+ /**
77
+ * Fragment threshold in bytes. Messages larger than this are fragmented.
78
+ * Set to 0 to disable fragmentation (not recommended for cloud deployments).
79
+ * Default: 100KB (safe for AWS API Gateway's 128KB limit)
80
+ */
81
+ fragmentThreshold?: number;
81
82
  }
82
83
  /**
83
84
  * Websocket server network adapter.
@@ -92,74 +93,75 @@ interface WebsocketServerTransportOptions {
92
93
  *
93
94
  * The connection handshake follows a two-phase protocol:
94
95
  * 1. Server sends text `"ready"` signal (transport-level)
95
- * 2. Client sends `establish-request` (protocol-level)
96
- * 3. Server responds with `establish-response` (handled by Synchronizer)
96
+ * 2. Client sends `establish` (protocol-level)
97
+ * 3. Server upgrades channel and sends present (handled by Synchronizer)
97
98
  *
98
99
  * The server does NOT call `establishChannel()` — it waits for the
99
- * client's establish-request to avoid a race condition where the binary
100
- * establish-request could arrive before the client has processed "ready".
100
+ * client's establish to avoid a race condition where the binary
101
+ * establish could arrive before the client has processed "ready".
101
102
  */
102
103
  declare class WebsocketServerTransport extends Transport<PeerId> {
103
- #private;
104
- constructor(options?: WebsocketServerTransportOptions);
105
- protected generate(peerId: PeerId): GeneratedChannel;
106
- onStart(): Promise<void>;
107
- onStop(): Promise<void>;
108
- /**
109
- * Handle a new Websocket connection.
110
- *
111
- * Call this from your framework's Websocket upgrade handler.
112
- * Returns a connection handle and a `start()` function that begins
113
- * message processing and sends the "ready" signal.
114
- *
115
- * @param options - Connection options including the Socket and optional peer ID
116
- * @returns A connection handle and start function
117
- *
118
- * @example Bun
119
- * ```typescript
120
- * const { start } = serverAdapter.handleConnection({
121
- * socket: wrapBunWebsocket(ws),
122
- * })
123
- * start()
124
- * ```
125
- *
126
- * @example Node.js ws
127
- * ```typescript
128
- * wss.on("connection", (ws) => {
129
- * const { start } = serverAdapter.handleConnection({
130
- * socket: wrapNodeWebsocket(ws),
131
- * })
132
- * start()
133
- * })
134
- * ```
135
- */
136
- handleConnection(options: WebsocketConnectionOptions): WebsocketConnectionResult;
137
- /**
138
- * Get an active connection by peer ID.
139
- */
140
- getConnection(peerId: PeerId): WebsocketConnection | undefined;
141
- /**
142
- * Get all active connections.
143
- */
144
- getAllConnections(): WebsocketConnection[];
145
- /**
146
- * Check if a peer is connected.
147
- */
148
- isConnected(peerId: PeerId): boolean;
149
- /**
150
- * Unregister a connection, removing its channel and cleaning up state.
151
- */
152
- unregisterConnection(peerId: PeerId): void;
153
- /**
154
- * Broadcast a message to all connected peers.
155
- */
156
- broadcast(msg: ChannelMsg): void;
157
- /**
158
- * Get the number of connected peers.
159
- */
160
- get connectionCount(): number;
104
+ #private;
105
+ constructor(options?: WebsocketServerTransportOptions);
106
+ protected generate(peerId: PeerId): GeneratedChannel;
107
+ onStart(): Promise<void>;
108
+ onStop(): Promise<void>;
109
+ /**
110
+ * Handle a new Websocket connection.
111
+ *
112
+ * Call this from your framework's Websocket upgrade handler.
113
+ * Returns a connection handle and a `start()` function that begins
114
+ * message processing and sends the "ready" signal.
115
+ *
116
+ * @param options - Connection options including the Socket and optional peer ID
117
+ * @returns A connection handle and start function
118
+ *
119
+ * @example Bun
120
+ * ```typescript
121
+ * const { start } = serverAdapter.handleConnection({
122
+ * socket: wrapBunWebsocket(ws),
123
+ * })
124
+ * start()
125
+ * ```
126
+ *
127
+ * @example Node.js ws
128
+ * ```typescript
129
+ * wss.on("connection", (ws) => {
130
+ * const { start } = serverAdapter.handleConnection({
131
+ * socket: wrapNodeWebsocket(ws),
132
+ * })
133
+ * start()
134
+ * })
135
+ * ```
136
+ */
137
+ handleConnection(options: WebsocketConnectionOptions): WebsocketConnectionResult;
138
+ /**
139
+ * Get an active connection by peer ID.
140
+ */
141
+ getConnection(peerId: PeerId): WebsocketConnection | undefined;
142
+ /**
143
+ * Get all active connections.
144
+ */
145
+ getAllConnections(): WebsocketConnection[];
146
+ /**
147
+ * Check if a peer is connected.
148
+ */
149
+ isConnected(peerId: PeerId): boolean;
150
+ /**
151
+ * Unregister a connection, removing its channel and cleaning up state.
152
+ */
153
+ unregisterConnection(peerId: PeerId): void;
154
+ /**
155
+ * Broadcast a message to all connected peers.
156
+ */
157
+ broadcast(msg: ChannelMsg): void;
158
+ /**
159
+ * Get the number of connected peers.
160
+ */
161
+ get connectionCount(): number;
161
162
  }
162
-
163
+ //#endregion
164
+ //#region src/service-client.d.ts
163
165
  /**
164
166
  * Options for service-to-service Websocket connections.
165
167
  *
@@ -194,5 +196,6 @@ type ServiceWebsocketClientOptions = WebsocketClientOptions;
194
196
  * ```
195
197
  */
196
198
  declare function createServiceWebsocketClient(options: ServiceWebsocketClientOptions): TransportFactory;
197
-
198
- export { DEFAULT_FRAGMENT_THRESHOLD, type ServiceWebsocketClientOptions, Socket, WebsocketConnection, type WebsocketConnectionConfig, WebsocketConnectionOptions, WebsocketConnectionResult, WebsocketServerTransport, type WebsocketServerTransportOptions, createServiceWebsocketClient };
199
+ //#endregion
200
+ export { DEFAULT_FRAGMENT_THRESHOLD, type DisconnectReason, type NodeWebsocketLike, READY_STATE, type ServiceWebsocketClientOptions, type Socket, type SocketReadyState, type WebSocketCloseEvent, type WebSocketConstructor, type WebSocketLike, type WebSocketMessageEvent, WebsocketConnection, type WebsocketConnectionConfig, type WebsocketConnectionHandle, type WebsocketConnectionOptions, type WebsocketConnectionResult, WebsocketServerTransport, type WebsocketServerTransportOptions, createServiceWebsocketClient, wrapNodeWebsocket, wrapStandardWebsocket };
201
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../src/connection.ts","../src/server-transport.ts","../src/service-client.ts"],"mappings":";;;;;;;;AA8BA;;cAAa,0BAAA;;;AAKb;UAAiB,yBAAA;;;;AAkBjB;;EAZE,iBAAA;AAAA;;;;;;;;;;cAYW,mBAAA;EAAA;WACF,MAAA,EAAQ,MAAA;EAAA,SACR,SAAA;cAgBP,MAAA,EAAQ,MAAA,EACR,SAAA,UACA,MAAA,EAAQ,MAAA,EACR,MAAA,GAAS,yBAAA;EAFT;;;;;EA4BF,WAAA,CAAY,OAAA,EAAS,OAAA;EAAA;;;;;;EAcrB,KAAA,CAAA;EAkDA;;;;;;EAjCA,IAAA,CAAK,GAAA,EAAK,UAAA;;AClFZ;;;;;AAiCA;EDwEE,SAAA,CAAA;;;;EAUA,KAAA,CAAM,IAAA,WAAe,MAAA;AAAA;;;;AAtIvB;;UCmBiB,+BAAA;EDnBmC;;AAKpD;;;ECoBE,iBAAA;AAAA;ADFF;;;;;;;;;;;;;;;;;;;;AAAA,cC6Ba,wBAAA,SAAiC,SAAA,CAAU,MAAA;EAAA;cAI1C,OAAA,GAAU,+BAAA;EAAA,UAUZ,QAAA,CAAS,MAAA,EAAQ,MAAA,GAAS,gBAAA;EAe9B,OAAA,CAAA,GAAW,OAAA;EAIX,MAAA,CAAA,GAAU,OAAA;EDDhB;;;;;;;;;;;;;ACjEF;;;;;AAiCA;;;;;;;;;;EAyEE,gBAAA,CACE,OAAA,EAAS,0BAAA,GACR,yBAAA;EA+DmB;;;EAAtB,aAAA,CAAc,MAAA,EAAQ,MAAA,GAAS,mBAAA;EAqBF;;;EAd7B,iBAAA,CAAA,GAAqB,mBAAA;EAjJgC;;;EAwJrD,WAAA,CAAY,MAAA,EAAQ,MAAA;;;;EAOpB,oBAAA,CAAqB,MAAA,EAAQ,MAAA;EAjJF;;;EA4J3B,SAAA,CAAU,GAAA,EAAK,UAAA;EA7IE;;;EAAA,IAsJb,eAAA,CAAA;AAAA;;;;;;ADvON;;;;;KETY,6BAAA,GAAgC,sBAAA;;;;;AFgC5C;;;;;;;;;;;;;;;;;;;;iBENgB,4BAAA,CACd,OAAA,EAAS,6BAAA,GACR,gBAAA"}