@roj-ai/transport 0.1.26 → 0.1.28

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.
@@ -38,10 +38,11 @@ export declare class ClientConnection<TReceive extends ProtocolDef, TSend extend
38
38
  hasReconnect(): boolean;
39
39
  protected handleOpen(): void;
40
40
  protected handleClose(code: number, reason: string): void;
41
- protected handleConnectionLost(): void;
41
+ protected handleConnectionLost(cause?: Error): void;
42
42
  private doConnect;
43
43
  private calculateReconnectDelay;
44
44
  private stopReconnect;
45
+ private rejectConnect;
45
46
  private clearConnectPromise;
46
47
  }
47
48
  export declare function createClientConnection<TReceive extends ProtocolDef, TSend extends ProtocolDef>(config: ClientConnectionConfig<TReceive, TSend>): ClientConnection<TReceive, TSend>;
@@ -1 +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"}
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;IAejC,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,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAkC5D,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,uBAAuB;IAS/B,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,aAAa;IAOrB,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"}
@@ -54,10 +54,7 @@ export class ClientConnection extends Connection {
54
54
  }
55
55
  this.ws = null;
56
56
  this.setState('disconnected');
57
- if (this.connectReject) {
58
- this.connectReject(new Error('Disconnected'));
59
- this.clearConnectPromise();
60
- }
57
+ this.rejectConnect(new Error('Disconnected'));
61
58
  }
62
59
  getReconnectAttempts() {
63
60
  return this.reconnectAttempts;
@@ -87,13 +84,14 @@ export class ClientConnection extends Connection {
87
84
  this.setState('disconnected');
88
85
  return;
89
86
  }
90
- if (this.connectReject) {
91
- this.connectReject(new Error(`Connection closed: ${code} ${reason}`));
92
- this.clearConnectPromise();
93
- }
94
- this.handleConnectionLost();
87
+ // Don't reject the pending connect() here. With reconnect enabled the initial
88
+ // connect must stay pending across failed attempts and settle only on open
89
+ // (resolve) or give-up (reject) — see handleConnectionLost. Rejecting on the
90
+ // first pre-open close crashed callers that await connect() under an
91
+ // infinite-reconnect policy.
92
+ this.handleConnectionLost(new Error(`Connection closed: ${code} ${reason}`));
95
93
  }
96
- handleConnectionLost() {
94
+ handleConnectionLost(cause) {
97
95
  this.cleanup();
98
96
  if (this.intentionalDisconnect) {
99
97
  this.setState('disconnected');
@@ -101,11 +99,13 @@ export class ClientConnection extends Connection {
101
99
  }
102
100
  if (!this.reconnectOptions || this.reconnectStopped) {
103
101
  this.setState('disconnected');
102
+ this.rejectConnect(cause ?? new Error('Connection lost'));
104
103
  return;
105
104
  }
106
105
  if (this.reconnectAttempts >= this.reconnectOptions.maxAttempts) {
107
106
  this.emit('error', new Error('Max reconnection attempts reached'));
108
107
  this.setState('disconnected');
108
+ this.rejectConnect(cause ?? new Error('Max reconnection attempts reached'));
109
109
  return;
110
110
  }
111
111
  this.setState('reconnecting');
@@ -126,11 +126,7 @@ export class ClientConnection extends Connection {
126
126
  ws.onopen = () => this.handleOpen();
127
127
  }
128
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();
129
+ this.handleConnectionLost(error instanceof Error ? error : new Error('Connection failed'));
134
130
  }
135
131
  }
136
132
  calculateReconnectDelay() {
@@ -149,6 +145,12 @@ export class ClientConnection extends Connection {
149
145
  this.reconnectTimer = null;
150
146
  }
151
147
  }
148
+ rejectConnect(error) {
149
+ if (this.connectReject) {
150
+ this.connectReject(error);
151
+ this.clearConnectPromise();
152
+ }
153
+ }
152
154
  clearConnectPromise() {
153
155
  this.connectPromise = null;
154
156
  this.connectResolve = null;
@@ -1 +1 @@
1
- {"version":3,"file":"client-connection.js","sourceRoot":"","sources":["../../src/client/client-connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"client-connection.js","sourceRoot":"","sources":["../../src/client/client-connection.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@roj-ai/transport",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "exports": {
@@ -0,0 +1,146 @@
1
+ /**
2
+ * ClientConnection reconnect / connect() settlement tests
3
+ *
4
+ * Regression guard: a WebSocket that closes *before it ever opens* must not
5
+ * reject the initial connect() promise while reconnect is still viable. The
6
+ * agent server awaits connect() during boot; rejecting on the first pre-open
7
+ * close crashed the whole process ("Process died") even though it was
8
+ * configured with an infinite-reconnect policy.
9
+ */
10
+
11
+ import { describe, expect, it } from 'bun:test'
12
+
13
+ import { ClientConnection } from './client-connection.js'
14
+ import type { ICloseEvent, IWebSocket, IWebSocketFactory } from '../platform/types.js'
15
+ import { WebSocketReadyState } from '../platform/types.js'
16
+
17
+ class MockWebSocket implements IWebSocket {
18
+ readyState: WebSocketReadyState = WebSocketReadyState.CONNECTING
19
+ onopen: (() => void) | null = null
20
+ onclose: ((event: ICloseEvent) => void) | null = null
21
+ onerror: ((error: Error) => void) | null = null
22
+ onmessage: ((data: string) => void) | null = null
23
+
24
+ send(_data: string): void {}
25
+
26
+ close(code = 1000, reason = ''): void {
27
+ this.readyState = WebSocketReadyState.CLOSED
28
+ this.onclose?.({ code, reason })
29
+ }
30
+
31
+ simulateOpen(): void {
32
+ this.readyState = WebSocketReadyState.OPEN
33
+ this.onopen?.()
34
+ }
35
+
36
+ simulateClose(code: number, reason: string): void {
37
+ this.readyState = WebSocketReadyState.CLOSED
38
+ this.onclose?.({ code, reason })
39
+ }
40
+ }
41
+
42
+ // Factory that fires open/close on the next tick — after ClientConnection has
43
+ // wired ws.onopen / ws.onclose in doConnect(). 'close' never opens the socket,
44
+ // reproducing a rejected upgrade / immediate server close on connect.
45
+ class MockWsFactory implements IWebSocketFactory {
46
+ created = 0
47
+ constructor(private readonly behavior: 'close' | 'open') {}
48
+
49
+ create(_url: string): IWebSocket {
50
+ this.created++
51
+ const ws = new MockWebSocket()
52
+ if (this.behavior === 'close') {
53
+ setTimeout(() => ws.simulateClose(1006, 'handshake failed'), 0)
54
+ } else {
55
+ setTimeout(() => ws.simulateOpen(), 0)
56
+ }
57
+ return ws
58
+ }
59
+ }
60
+
61
+ const tick = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms))
62
+
63
+ describe('ClientConnection connect() settlement on pre-open close', () => {
64
+ it('keeps connect() pending (no reject) across pre-open closes under infinite reconnect', async () => {
65
+ const factory = new MockWsFactory('close')
66
+ const client = new ClientConnection({
67
+ url: 'ws://mock',
68
+ wsFactory: factory,
69
+ reconnect: { baseDelayMs: 10, maxDelayMs: 20, maxAttempts: Infinity, jitterFactor: 0 },
70
+ })
71
+
72
+ let settled: 'resolved' | 'rejected' | null = null
73
+ const reconnecting: number[] = []
74
+ client.on((event, data) => {
75
+ if (event === 'reconnecting') reconnecting.push((data as { attempt: number }).attempt)
76
+ })
77
+
78
+ const p = client.connect().then(
79
+ () => { settled = 'resolved' },
80
+ () => { settled = 'rejected' },
81
+ )
82
+
83
+ await tick(80)
84
+
85
+ // The crash used to happen here: connect() must NOT have rejected yet.
86
+ expect(settled).toBeNull()
87
+ expect(factory.created).toBeGreaterThan(1) // kept retrying
88
+ expect(reconnecting.length).toBeGreaterThan(1)
89
+
90
+ // disconnect() is the only thing that settles a still-pending connect()
91
+ await client.disconnect()
92
+ await p
93
+ expect(settled).toBe('rejected')
94
+ })
95
+
96
+ it('resolves connect() once a later attempt opens (recovers after transient closes)', async () => {
97
+ // Fails the first two attempts (pre-open close), then opens.
98
+ let attempt = 0
99
+ const factory: IWebSocketFactory = {
100
+ create: () => {
101
+ attempt++
102
+ const ws = new MockWebSocket()
103
+ if (attempt <= 2) {
104
+ setTimeout(() => ws.simulateClose(1006, 'handshake failed'), 0)
105
+ } else {
106
+ setTimeout(() => ws.simulateOpen(), 0)
107
+ }
108
+ return ws
109
+ },
110
+ }
111
+ const client = new ClientConnection({
112
+ url: 'ws://mock',
113
+ wsFactory: factory,
114
+ reconnect: { baseDelayMs: 10, maxDelayMs: 20, maxAttempts: Infinity, jitterFactor: 0 },
115
+ })
116
+
117
+ await client.connect()
118
+ expect(client.isConnected()).toBe(true)
119
+
120
+ await client.disconnect()
121
+ })
122
+
123
+ it('rejects connect() after a finite reconnect budget is exhausted', async () => {
124
+ const factory = new MockWsFactory('close')
125
+ const client = new ClientConnection({
126
+ url: 'ws://mock',
127
+ wsFactory: factory,
128
+ reconnect: { baseDelayMs: 5, maxDelayMs: 10, maxAttempts: 2, jitterFactor: 0 },
129
+ })
130
+
131
+ await expect(client.connect()).rejects.toThrow(/Connection closed/)
132
+ expect(factory.created).toBe(3) // initial attempt + 2 reconnects
133
+ })
134
+
135
+ it('rejects connect() on the first pre-open close when reconnect is disabled', async () => {
136
+ const factory = new MockWsFactory('close')
137
+ const client = new ClientConnection({
138
+ url: 'ws://mock',
139
+ wsFactory: factory,
140
+ // no reconnect option → reconnect disabled
141
+ })
142
+
143
+ await expect(client.connect()).rejects.toThrow(/Connection closed/)
144
+ expect(factory.created).toBe(1)
145
+ })
146
+ })
@@ -78,10 +78,7 @@ export class ClientConnection<TReceive extends ProtocolDef, TSend extends Protoc
78
78
  this.ws = null
79
79
  this.setState('disconnected')
80
80
 
81
- if (this.connectReject) {
82
- this.connectReject(new Error('Disconnected'))
83
- this.clearConnectPromise()
84
- }
81
+ this.rejectConnect(new Error('Disconnected'))
85
82
  }
86
83
 
87
84
  getReconnectAttempts(): number {
@@ -120,15 +117,15 @@ export class ClientConnection<TReceive extends ProtocolDef, TSend extends Protoc
120
117
  return
121
118
  }
122
119
 
123
- if (this.connectReject) {
124
- this.connectReject(new Error(`Connection closed: ${code} ${reason}`))
125
- this.clearConnectPromise()
126
- }
127
-
128
- this.handleConnectionLost()
120
+ // Don't reject the pending connect() here. With reconnect enabled the initial
121
+ // connect must stay pending across failed attempts and settle only on open
122
+ // (resolve) or give-up (reject) — see handleConnectionLost. Rejecting on the
123
+ // first pre-open close crashed callers that await connect() under an
124
+ // infinite-reconnect policy.
125
+ this.handleConnectionLost(new Error(`Connection closed: ${code} ${reason}`))
129
126
  }
130
127
 
131
- protected override handleConnectionLost(): void {
128
+ protected override handleConnectionLost(cause?: Error): void {
132
129
  this.cleanup()
133
130
 
134
131
  if (this.intentionalDisconnect) {
@@ -138,12 +135,14 @@ export class ClientConnection<TReceive extends ProtocolDef, TSend extends Protoc
138
135
 
139
136
  if (!this.reconnectOptions || this.reconnectStopped) {
140
137
  this.setState('disconnected')
138
+ this.rejectConnect(cause ?? new Error('Connection lost'))
141
139
  return
142
140
  }
143
141
 
144
142
  if (this.reconnectAttempts >= this.reconnectOptions.maxAttempts) {
145
143
  this.emit('error', new Error('Max reconnection attempts reached'))
146
144
  this.setState('disconnected')
145
+ this.rejectConnect(cause ?? new Error('Max reconnection attempts reached'))
147
146
  return
148
147
  }
149
148
 
@@ -168,11 +167,7 @@ export class ClientConnection<TReceive extends ProtocolDef, TSend extends Protoc
168
167
  this.setupWebSocket(ws)
169
168
  ws.onopen = () => this.handleOpen()
170
169
  } catch (error) {
171
- if (this.connectReject) {
172
- this.connectReject(error instanceof Error ? error : new Error('Connection failed'))
173
- this.clearConnectPromise()
174
- }
175
- this.handleConnectionLost()
170
+ this.handleConnectionLost(error instanceof Error ? error : new Error('Connection failed'))
176
171
  }
177
172
  }
178
173
 
@@ -193,6 +188,13 @@ export class ClientConnection<TReceive extends ProtocolDef, TSend extends Protoc
193
188
  }
194
189
  }
195
190
 
191
+ private rejectConnect(error: Error): void {
192
+ if (this.connectReject) {
193
+ this.connectReject(error)
194
+ this.clearConnectPromise()
195
+ }
196
+ }
197
+
196
198
  private clearConnectPromise(): void {
197
199
  this.connectPromise = null
198
200
  this.connectResolve = null