@roj-ai/transport 0.0.2

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.
Files changed (64) hide show
  1. package/dist/client/client-connection.d.ts +48 -0
  2. package/dist/client/client-connection.d.ts.map +1 -0
  3. package/dist/client/client-connection.js +160 -0
  4. package/dist/client/index.d.ts +10 -0
  5. package/dist/client/index.d.ts.map +1 -0
  6. package/dist/client/index.js +7 -0
  7. package/dist/core/connection.d.ts +59 -0
  8. package/dist/core/connection.d.ts.map +1 -0
  9. package/dist/core/connection.js +165 -0
  10. package/dist/core/index.d.ts +16 -0
  11. package/dist/core/index.d.ts.map +1 -0
  12. package/dist/core/index.js +13 -0
  13. package/dist/core/protocol.d.ts +206 -0
  14. package/dist/core/protocol.d.ts.map +1 -0
  15. package/dist/core/protocol.js +93 -0
  16. package/dist/core/result.d.ts +26 -0
  17. package/dist/core/result.d.ts.map +1 -0
  18. package/dist/core/result.js +20 -0
  19. package/dist/core/router.d.ts +79 -0
  20. package/dist/core/router.d.ts.map +1 -0
  21. package/dist/core/router.js +119 -0
  22. package/dist/core/types.d.ts +44 -0
  23. package/dist/core/types.d.ts.map +1 -0
  24. package/dist/core/types.js +14 -0
  25. package/dist/index.d.ts +24 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +26 -0
  28. package/dist/platform/browser.d.ts +14 -0
  29. package/dist/platform/browser.d.ts.map +1 -0
  30. package/dist/platform/browser.js +65 -0
  31. package/dist/platform/bun.d.ts +69 -0
  32. package/dist/platform/bun.d.ts.map +1 -0
  33. package/dist/platform/bun.js +152 -0
  34. package/dist/platform/types.d.ts +83 -0
  35. package/dist/platform/types.d.ts.map +1 -0
  36. package/dist/platform/types.js +14 -0
  37. package/dist/server/connection-manager.d.ts +106 -0
  38. package/dist/server/connection-manager.d.ts.map +1 -0
  39. package/dist/server/connection-manager.js +234 -0
  40. package/dist/server/index.d.ts +12 -0
  41. package/dist/server/index.d.ts.map +1 -0
  42. package/dist/server/index.js +8 -0
  43. package/dist/server/server-connection.d.ts +50 -0
  44. package/dist/server/server-connection.d.ts.map +1 -0
  45. package/dist/server/server-connection.js +84 -0
  46. package/package.json +52 -0
  47. package/src/__tests__/integration.test.ts +443 -0
  48. package/src/client/client-connection.ts +207 -0
  49. package/src/client/index.ts +24 -0
  50. package/src/core/connection.ts +203 -0
  51. package/src/core/index.ts +52 -0
  52. package/src/core/protocol.test.ts +235 -0
  53. package/src/core/protocol.ts +348 -0
  54. package/src/core/result.ts +35 -0
  55. package/src/core/router.test.ts +248 -0
  56. package/src/core/router.ts +186 -0
  57. package/src/core/types.ts +85 -0
  58. package/src/index.ts +80 -0
  59. package/src/platform/browser.ts +79 -0
  60. package/src/platform/bun.ts +220 -0
  61. package/src/platform/types.ts +102 -0
  62. package/src/server/connection-manager.ts +298 -0
  63. package/src/server/index.ts +26 -0
  64. package/src/server/server-connection.ts +122 -0
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Client Connection
3
+ *
4
+ * WebSocket client with automatic reconnection.
5
+ * Simplified: no handshake - connected immediately when WebSocket opens.
6
+ */
7
+ import { Connection } from '../core/connection.js';
8
+ import type { ConnectionConfig } from '../core/connection.js';
9
+ import type { ProtocolDef } from '../core/protocol.js';
10
+ import type { ReconnectOptions } from '../core/types.js';
11
+ import type { IWebSocketFactory } from '../platform/types.js';
12
+ /**
13
+ * Client connection configuration.
14
+ */
15
+ export interface ClientConnectionConfig<TReceive extends ProtocolDef, TSend extends ProtocolDef> extends ConnectionConfig<TReceive, TSend> {
16
+ url: string;
17
+ wsFactory: IWebSocketFactory;
18
+ reconnect?: Partial<ReconnectOptions>;
19
+ }
20
+ /**
21
+ * WebSocket client with automatic reconnection and exponential backoff.
22
+ */
23
+ export declare class ClientConnection<TReceive extends ProtocolDef, TSend extends ProtocolDef> extends Connection<TReceive, TSend> {
24
+ private readonly url;
25
+ private readonly wsFactory;
26
+ private readonly reconnectOptions;
27
+ private reconnectAttempts;
28
+ private reconnectTimer;
29
+ private reconnectStopped;
30
+ private connectPromise;
31
+ private connectResolve;
32
+ private connectReject;
33
+ private intentionalDisconnect;
34
+ constructor(config: ClientConnectionConfig<TReceive, TSend>);
35
+ connect(): Promise<void>;
36
+ disconnect(): Promise<void>;
37
+ getReconnectAttempts(): number;
38
+ hasReconnect(): boolean;
39
+ protected handleOpen(): void;
40
+ protected handleClose(code: number, reason: string): void;
41
+ protected handleConnectionLost(): void;
42
+ private doConnect;
43
+ private calculateReconnectDelay;
44
+ private stopReconnect;
45
+ private clearConnectPromise;
46
+ }
47
+ export declare function createClientConnection<TReceive extends ProtocolDef, TSend extends ProtocolDef>(config: ClientConnectionConfig<TReceive, TSend>): ClientConnection<TReceive, TSend>;
48
+ //# sourceMappingURL=client-connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-connection.d.ts","sourceRoot":"","sources":["../../src/client/client-connection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAG7D;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,QAAQ,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW,CAAE,SAAQ,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzI,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,iBAAiB,CAAA;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;CACrC;AAED;;GAEG;AACH,qBAAa,gBAAgB,CAAC,QAAQ,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW,CAAE,SAAQ,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAmB;IAG7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAyB;IAC1D,OAAO,CAAC,iBAAiB,CAAI;IAC7B,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,gBAAgB,CAAQ;IAGhC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,aAAa,CAAwC;IAC7D,OAAO,CAAC,qBAAqB,CAAQ;gBAEzB,MAAM,EAAE,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC;IASrD,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgBxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBjC,oBAAoB,IAAI,MAAM;IAI9B,YAAY,IAAI,OAAO;cAKJ,UAAU,IAAI,IAAI;cAmBlB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;cAgB/C,oBAAoB,IAAI,IAAI;IAgC/C,OAAO,CAAC,SAAS;IAgBjB,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,mBAAmB;CAK3B;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW,EAC7F,MAAM,EAAE,sBAAsB,CAAC,QAAQ,EAAE,KAAK,CAAC,GAC7C,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAEnC"}
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Client Connection
3
+ *
4
+ * WebSocket client with automatic reconnection.
5
+ * Simplified: no handshake - connected immediately when WebSocket opens.
6
+ */
7
+ import { Connection } from '../core/connection.js';
8
+ import { DEFAULT_RECONNECT_OPTIONS } from '../core/types.js';
9
+ import { WebSocketReadyState } from '../platform/types.js';
10
+ /**
11
+ * WebSocket client with automatic reconnection and exponential backoff.
12
+ */
13
+ export class ClientConnection extends Connection {
14
+ url;
15
+ wsFactory;
16
+ // Reconnection
17
+ reconnectOptions;
18
+ reconnectAttempts = 0;
19
+ reconnectTimer = null;
20
+ reconnectStopped = false;
21
+ // Connect promise
22
+ connectPromise = null;
23
+ connectResolve = null;
24
+ connectReject = null;
25
+ intentionalDisconnect = false;
26
+ constructor(config) {
27
+ super(config);
28
+ this.url = config.url;
29
+ this.wsFactory = config.wsFactory;
30
+ this.reconnectOptions = config.reconnect
31
+ ? { ...DEFAULT_RECONNECT_OPTIONS, ...config.reconnect }
32
+ : null;
33
+ }
34
+ async connect() {
35
+ if (this.isConnected())
36
+ return;
37
+ if (this.connectPromise)
38
+ return this.connectPromise;
39
+ this.intentionalDisconnect = false;
40
+ this.reconnectStopped = false;
41
+ this.connectPromise = new Promise((resolve, reject) => {
42
+ this.connectResolve = resolve;
43
+ this.connectReject = reject;
44
+ this.doConnect();
45
+ });
46
+ return this.connectPromise;
47
+ }
48
+ async disconnect() {
49
+ this.intentionalDisconnect = true;
50
+ this.stopReconnect();
51
+ this.cleanup();
52
+ if (this.ws && this.ws.readyState !== WebSocketReadyState.CLOSED) {
53
+ this.ws.close(1000, 'Client disconnect');
54
+ }
55
+ this.ws = null;
56
+ this.setState('disconnected');
57
+ if (this.connectReject) {
58
+ this.connectReject(new Error('Disconnected'));
59
+ this.clearConnectPromise();
60
+ }
61
+ }
62
+ getReconnectAttempts() {
63
+ return this.reconnectAttempts;
64
+ }
65
+ hasReconnect() {
66
+ return this.reconnectOptions !== null;
67
+ }
68
+ // Override to resolve connect promise and reset reconnect
69
+ handleOpen() {
70
+ const wasReconnecting = this.reconnectAttempts > 0;
71
+ super.handleOpen();
72
+ this.reconnectAttempts = 0;
73
+ // Flush any messages buffered during disconnect
74
+ this.flushSendBuffer();
75
+ if (this.connectResolve) {
76
+ this.connectResolve();
77
+ this.clearConnectPromise();
78
+ }
79
+ // Emit reconnected event so application can restore subscriptions
80
+ if (wasReconnecting) {
81
+ this.emit('reconnected');
82
+ }
83
+ }
84
+ handleClose(code, reason) {
85
+ this.cleanup();
86
+ if (this.intentionalDisconnect) {
87
+ this.setState('disconnected');
88
+ return;
89
+ }
90
+ if (this.connectReject) {
91
+ this.connectReject(new Error(`Connection closed: ${code} ${reason}`));
92
+ this.clearConnectPromise();
93
+ }
94
+ this.handleConnectionLost();
95
+ }
96
+ handleConnectionLost() {
97
+ this.cleanup();
98
+ if (this.intentionalDisconnect) {
99
+ this.setState('disconnected');
100
+ return;
101
+ }
102
+ if (!this.reconnectOptions || this.reconnectStopped) {
103
+ this.setState('disconnected');
104
+ return;
105
+ }
106
+ if (this.reconnectAttempts >= this.reconnectOptions.maxAttempts) {
107
+ this.emit('error', new Error('Max reconnection attempts reached'));
108
+ this.setState('disconnected');
109
+ return;
110
+ }
111
+ this.setState('reconnecting');
112
+ const delay = this.calculateReconnectDelay();
113
+ this.reconnectAttempts++;
114
+ this.emit('reconnecting', { delay, attempt: this.reconnectAttempts });
115
+ this.reconnectTimer = setTimeout(() => {
116
+ this.reconnectTimer = null;
117
+ this.setState('connecting');
118
+ this.doConnect();
119
+ }, delay);
120
+ }
121
+ doConnect() {
122
+ this.setState('connecting');
123
+ try {
124
+ const ws = this.wsFactory.create(this.url);
125
+ this.setupWebSocket(ws);
126
+ ws.onopen = () => this.handleOpen();
127
+ }
128
+ catch (error) {
129
+ if (this.connectReject) {
130
+ this.connectReject(error instanceof Error ? error : new Error('Connection failed'));
131
+ this.clearConnectPromise();
132
+ }
133
+ this.handleConnectionLost();
134
+ }
135
+ }
136
+ calculateReconnectDelay() {
137
+ if (!this.reconnectOptions)
138
+ return 0;
139
+ const { baseDelayMs, maxDelayMs, jitterFactor } = this.reconnectOptions;
140
+ const exponential = baseDelayMs * Math.pow(2, this.reconnectAttempts);
141
+ const capped = Math.min(exponential, maxDelayMs);
142
+ const jitter = capped * jitterFactor * Math.random();
143
+ return Math.floor(capped + jitter);
144
+ }
145
+ stopReconnect() {
146
+ this.reconnectStopped = true;
147
+ if (this.reconnectTimer) {
148
+ clearTimeout(this.reconnectTimer);
149
+ this.reconnectTimer = null;
150
+ }
151
+ }
152
+ clearConnectPromise() {
153
+ this.connectPromise = null;
154
+ this.connectResolve = null;
155
+ this.connectReject = null;
156
+ }
157
+ }
158
+ export function createClientConnection(config) {
159
+ return new ClientConnection(config);
160
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Client Connection Module
3
+ *
4
+ * WebSocket client for connecting to server endpoints.
5
+ */
6
+ export { ClientConnection, createClientConnection } from './client-connection.js';
7
+ export type { ClientConnectionConfig } from './client-connection.js';
8
+ export type { ConnectionState, HandlerContext, Protocol, ProtocolCaller, ProtocolDef, ProtocolHandlers, ProtocolNotifier, ReconnectOptions, TransportEvent, TransportEventListener, } from '../core/index.js';
9
+ export { defineProtocol, method, notification } from '../core/index.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AACjF,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAA;AAGpE,YAAY,EACX,eAAe,EACf,cAAc,EACd,QAAQ,EACR,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,sBAAsB,GACtB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Client Connection Module
3
+ *
4
+ * WebSocket client for connecting to server endpoints.
5
+ */
6
+ export { ClientConnection, createClientConnection } from './client-connection.js';
7
+ export { defineProtocol, method, notification } from '../core/index.js';
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Connection Base Class
3
+ *
4
+ * Provides symmetric API for WebSocket client and server connections.
5
+ * Simplified: no handshake, no heartbeat - just WebSocket open/close → connected/disconnected.
6
+ * Notification-only: no request-response, no correlation.
7
+ */
8
+ import type { IWebSocket } from '../platform/types.js';
9
+ import type { Protocol, ProtocolDef, ProtocolHandlers, ProtocolNotifier } from './protocol.js';
10
+ import { MessageRouter } from './router.js';
11
+ import type { RawMessageListener } from './router.js';
12
+ import type { ConnectionState, TransportEvent, TransportEventListener } from './types.js';
13
+ /**
14
+ * Connection configuration.
15
+ */
16
+ export interface ConnectionConfig<TReceive extends ProtocolDef, TSend extends ProtocolDef> {
17
+ receiveProtocol?: Protocol<TReceive>;
18
+ sendProtocol?: Protocol<TSend>;
19
+ }
20
+ /**
21
+ * Abstract base class for WebSocket connections.
22
+ * State transitions: disconnected ↔ connecting ↔ connected ↔ reconnecting
23
+ */
24
+ export declare abstract class Connection<TReceive extends ProtocolDef, TSend extends ProtocolDef> {
25
+ protected readonly config: ConnectionConfig<TReceive, TSend>;
26
+ protected readonly router: MessageRouter<TReceive, TSend>;
27
+ protected state: ConnectionState;
28
+ protected ws: IWebSocket | null;
29
+ protected connectionId: string;
30
+ protected eventListeners: Set<TransportEventListener>;
31
+ constructor(config: ConnectionConfig<TReceive, TSend>);
32
+ getState(): ConnectionState;
33
+ isConnected(): boolean;
34
+ getConnectionId(): string;
35
+ setHandlers(handlers: Partial<ProtocolHandlers<TReceive>>): void;
36
+ /**
37
+ * Set a raw message listener that receives every incoming message as (type, payload).
38
+ * When set, bypasses per-type handler dispatch.
39
+ */
40
+ setRawMessageListener(listener: RawMessageListener): void;
41
+ get notify(): ProtocolNotifier<TSend>;
42
+ private sendBuffer;
43
+ private readonly maxBufferSize;
44
+ send(data: string): boolean;
45
+ flushSendBuffer(): void;
46
+ clearSendBuffer(): void;
47
+ get bufferedMessageCount(): number;
48
+ on(listener: TransportEventListener): () => void;
49
+ abstract connect(): Promise<void>;
50
+ abstract disconnect(): Promise<void>;
51
+ protected setState(newState: ConnectionState): void;
52
+ protected setupWebSocket(ws: IWebSocket): void;
53
+ protected handleOpen(): void;
54
+ protected handleClose(_code: number, _reason: string): void;
55
+ protected handleConnectionLost(): void;
56
+ protected cleanup(): void;
57
+ protected emit(event: TransportEvent, data?: unknown): void;
58
+ }
59
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/core/connection.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAC9F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AACrD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAA;AAEzF;;GAEG;AACH,MAAM,WAAW,gBAAgB,CAAC,QAAQ,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW;IACxF,eAAe,CAAC,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAA;IACpC,YAAY,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;CAC9B;AAED;;;GAGG;AACH,8BAAsB,UAAU,CAAC,QAAQ,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW;IACvF,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAC5D,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IAGzD,SAAS,CAAC,KAAK,EAAE,eAAe,CAAiB;IACjD,SAAS,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI,CAAO;IACtC,SAAS,CAAC,YAAY,EAAE,MAAM,CAAA;IAC9B,SAAS,CAAC,cAAc,8BAAoC;gBAEhD,MAAM,EAAE,gBAAgB,CAAC,QAAQ,EAAE,KAAK,CAAC;IAerD,QAAQ,IAAI,eAAe;IAI3B,WAAW,IAAI,OAAO;IAItB,eAAe,IAAI,MAAM;IAIzB,WAAW,CAAC,QAAQ,EAAE,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,GAAG,IAAI;IAIhE;;;OAGG;IACH,qBAAqB,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAIzD,IAAI,MAAM,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAUpC;IAMD,OAAO,CAAC,UAAU,CAAe;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAc;IAE5C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAmB3B,eAAe,IAAI,IAAI;IAavB,eAAe,IAAI,IAAI;IAIvB,IAAI,oBAAoB,IAAI,MAAM,CAEjC;IAED,EAAE,CAAC,QAAQ,EAAE,sBAAsB,GAAG,MAAM,IAAI;IAKhD,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMpC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAcnD,SAAS,CAAC,cAAc,CAAC,EAAE,EAAE,UAAU,GAAG,IAAI;IAQ9C,SAAS,CAAC,UAAU,IAAI,IAAI;IAK5B,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAK3D,SAAS,CAAC,oBAAoB,IAAI,IAAI;IAKtC,SAAS,CAAC,OAAO,IAAI,IAAI;IASzB,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,IAAI;CAS3D"}
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Connection Base Class
3
+ *
4
+ * Provides symmetric API for WebSocket client and server connections.
5
+ * Simplified: no handshake, no heartbeat - just WebSocket open/close → connected/disconnected.
6
+ * Notification-only: no request-response, no correlation.
7
+ */
8
+ import { WebSocketReadyState } from '../platform/types.js';
9
+ import { MessageRouter } from './router.js';
10
+ /**
11
+ * Abstract base class for WebSocket connections.
12
+ * State transitions: disconnected ↔ connecting ↔ connected ↔ reconnecting
13
+ */
14
+ export class Connection {
15
+ config;
16
+ router;
17
+ // State
18
+ state = 'disconnected';
19
+ ws = null;
20
+ connectionId;
21
+ eventListeners = new Set();
22
+ constructor(config) {
23
+ this.config = config;
24
+ this.connectionId = `conn-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
25
+ this.router = new MessageRouter(config.receiveProtocol ?? null, config.sendProtocol ?? null);
26
+ this.router.setErrorHandler((error) => this.emit('router_error', error));
27
+ }
28
+ // ============================================================================
29
+ // Public API
30
+ // ============================================================================
31
+ getState() {
32
+ return this.state;
33
+ }
34
+ isConnected() {
35
+ return this.state === 'connected';
36
+ }
37
+ getConnectionId() {
38
+ return this.connectionId;
39
+ }
40
+ setHandlers(handlers) {
41
+ this.router.setHandlers(handlers);
42
+ }
43
+ /**
44
+ * Set a raw message listener that receives every incoming message as (type, payload).
45
+ * When set, bypasses per-type handler dispatch.
46
+ */
47
+ setRawMessageListener(listener) {
48
+ this.router.setRawMessageListener(listener);
49
+ }
50
+ get notify() {
51
+ const protocol = this.config.sendProtocol;
52
+ if (!protocol) {
53
+ throw new Error('Cannot send notifications without a send protocol');
54
+ }
55
+ const notifier = {};
56
+ for (const name of protocol.getNotificationNames()) {
57
+ notifier[name] = (input) => this.router.notify(name, input);
58
+ }
59
+ return notifier;
60
+ }
61
+ // ============================================================================
62
+ // Send Buffer (bridges short disconnects)
63
+ // ============================================================================
64
+ sendBuffer = [];
65
+ maxBufferSize = 500;
66
+ send(data) {
67
+ if (!this.ws || this.ws.readyState !== WebSocketReadyState.OPEN) {
68
+ if (this.sendBuffer.length < this.maxBufferSize) {
69
+ this.sendBuffer.push(data);
70
+ }
71
+ return false;
72
+ }
73
+ this.flushSendBuffer();
74
+ try {
75
+ this.ws.send(data);
76
+ return true;
77
+ }
78
+ catch {
79
+ if (this.sendBuffer.length < this.maxBufferSize) {
80
+ this.sendBuffer.push(data);
81
+ }
82
+ return false;
83
+ }
84
+ }
85
+ flushSendBuffer() {
86
+ while (this.sendBuffer.length > 0) {
87
+ if (!this.ws || this.ws.readyState !== WebSocketReadyState.OPEN)
88
+ break;
89
+ const msg = this.sendBuffer.shift();
90
+ try {
91
+ this.ws.send(msg);
92
+ }
93
+ catch {
94
+ this.sendBuffer.unshift(msg);
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ clearSendBuffer() {
100
+ this.sendBuffer.length = 0;
101
+ }
102
+ get bufferedMessageCount() {
103
+ return this.sendBuffer.length;
104
+ }
105
+ on(listener) {
106
+ this.eventListeners.add(listener);
107
+ return () => this.eventListeners.delete(listener);
108
+ }
109
+ // ============================================================================
110
+ // State Management
111
+ // ============================================================================
112
+ setState(newState) {
113
+ if (newState === this.state)
114
+ return;
115
+ const from = this.state;
116
+ this.state = newState;
117
+ this.emit('state_change', { from, to: newState });
118
+ if (newState === 'connected')
119
+ this.emit('connected');
120
+ else if (newState === 'disconnected')
121
+ this.emit('disconnected');
122
+ else if (newState === 'reconnecting')
123
+ this.emit('reconnecting');
124
+ }
125
+ // ============================================================================
126
+ // WebSocket Setup
127
+ // ============================================================================
128
+ setupWebSocket(ws) {
129
+ this.ws = ws;
130
+ this.router.setSender((message) => this.send(message));
131
+ ws.onmessage = (data) => this.router.handleMessage(data, { connectionId: this.connectionId });
132
+ ws.onerror = (error) => this.emit('error', error);
133
+ ws.onclose = (event) => this.handleClose(event.code, event.reason);
134
+ }
135
+ handleOpen() {
136
+ // Immediately connected - no handshake needed
137
+ this.setState('connected');
138
+ }
139
+ handleClose(_code, _reason) {
140
+ this.cleanup();
141
+ this.setState('disconnected');
142
+ }
143
+ handleConnectionLost() {
144
+ this.cleanup();
145
+ this.setState('disconnected');
146
+ }
147
+ cleanup() {
148
+ if (this.ws) {
149
+ this.ws.onopen = null;
150
+ this.ws.onclose = null;
151
+ this.ws.onerror = null;
152
+ this.ws.onmessage = null;
153
+ }
154
+ }
155
+ emit(event, data) {
156
+ for (const listener of this.eventListeners) {
157
+ try {
158
+ listener(event, data);
159
+ }
160
+ catch {
161
+ // Ignore listener errors
162
+ }
163
+ }
164
+ }
165
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Core Transport Module
3
+ *
4
+ * Platform-agnostic WebSocket infrastructure.
5
+ */
6
+ export type { ConnectionState, ReconnectOptions, TransportEvent, TransportEventListener, WireMessage } from './types.js';
7
+ export { DEFAULT_RECONNECT_OPTIONS } from './types.js';
8
+ export type { Result } from './result.js';
9
+ export { Err, flatMapResult, isErr, isOk, mapResult, Ok, unwrapOr, unwrapOrThrow } from './result.js';
10
+ export { defineProtocol, method, notification } from './protocol.js';
11
+ export type { CallerFor, EndpointDef, EndpointHandler, ExtractProtocolDef, HandlerContext, HandlersFor, InferError, InferInput, InferOutput, IsMethod, IsNotification, MethodCaller, MethodDef, MethodHandler, MethodOptions, NotificationDef, NotificationHandler, NotificationNotifier, NotificationOptions, NotifierFor, Protocol, ProtocolCaller, ProtocolDef, ProtocolHandlers, ProtocolNotifier, } from './protocol.js';
12
+ export { createRouter, MessageRouter } from './router.js';
13
+ export type { MessageSender } from './router.js';
14
+ export { Connection } from './connection.js';
15
+ export type { ConnectionConfig } from './connection.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAExH,OAAO,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAGtD,YAAY,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGrG,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACpE,YAAY,EACX,SAAS,EACT,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,WAAW,EACX,UAAU,EACV,UAAU,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,SAAS,EACT,aAAa,EACb,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,WAAW,EACX,QAAQ,EACR,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AACzD,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Core Transport Module
3
+ *
4
+ * Platform-agnostic WebSocket infrastructure.
5
+ */
6
+ export { DEFAULT_RECONNECT_OPTIONS } from './types.js';
7
+ export { Err, flatMapResult, isErr, isOk, mapResult, Ok, unwrapOr, unwrapOrThrow } from './result.js';
8
+ // Protocol Definition
9
+ export { defineProtocol, method, notification } from './protocol.js';
10
+ // Message Router
11
+ export { createRouter, MessageRouter } from './router.js';
12
+ // Connection Base
13
+ export { Connection } from './connection.js';