@milaboratories/pl-client 2.16.12 → 2.16.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/driver.cjs +1 -1
- package/dist/core/driver.cjs.map +1 -1
- package/dist/core/driver.js +1 -1
- package/dist/core/driver.js.map +1 -1
- package/dist/core/ll_client.cjs +17 -7
- package/dist/core/ll_client.cjs.map +1 -1
- package/dist/core/ll_client.d.ts.map +1 -1
- package/dist/core/ll_client.js +17 -7
- package/dist/core/ll_client.js.map +1 -1
- package/dist/core/websocket_stream.cjs +126 -129
- package/dist/core/websocket_stream.cjs.map +1 -1
- package/dist/core/websocket_stream.d.ts +29 -22
- package/dist/core/websocket_stream.d.ts.map +1 -1
- package/dist/core/websocket_stream.js +127 -130
- package/dist/core/websocket_stream.js.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs +136 -0
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts +75 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.d.ts.map +1 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js +135 -1
- package/dist/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.js.map +1 -1
- package/dist/proto-rest/index.cjs +16 -2
- package/dist/proto-rest/index.cjs.map +1 -1
- package/dist/proto-rest/index.d.ts.map +1 -1
- package/dist/proto-rest/index.js +16 -2
- package/dist/proto-rest/index.js.map +1 -1
- package/package.json +6 -6
- package/src/core/driver.ts +1 -1
- package/src/core/ll_client.ts +25 -8
- package/src/core/websocket_stream.test.ts +19 -8
- package/src/core/websocket_stream.ts +154 -166
- package/src/proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.ts +179 -1
- package/src/proto-rest/index.ts +17 -2
|
@@ -1,181 +1,168 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var undici = require('undici');
|
|
4
|
-
var api = require('../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api.cjs');
|
|
5
4
|
var Denque = require('denque');
|
|
6
5
|
var retry_strategy = require('../helpers/retry_strategy.cjs');
|
|
7
6
|
|
|
7
|
+
var ConnectionState;
|
|
8
|
+
(function (ConnectionState) {
|
|
9
|
+
ConnectionState[ConnectionState["NEW"] = 0] = "NEW";
|
|
10
|
+
ConnectionState[ConnectionState["CONNECTING"] = 1] = "CONNECTING";
|
|
11
|
+
ConnectionState[ConnectionState["CONNECTED"] = 2] = "CONNECTED";
|
|
12
|
+
ConnectionState[ConnectionState["CLOSING"] = 3] = "CLOSING";
|
|
13
|
+
ConnectionState[ConnectionState["CLOSED"] = 4] = "CLOSED";
|
|
14
|
+
})(ConnectionState || (ConnectionState = {}));
|
|
8
15
|
/**
|
|
9
16
|
* WebSocket-based bidirectional stream implementation for LLTransaction.
|
|
10
17
|
* Implements BiDiStream interface which is compatible with DuplexStreamingCall.
|
|
11
18
|
*/
|
|
12
19
|
class WebSocketBiDiStream {
|
|
20
|
+
url;
|
|
21
|
+
serializeClientMessage;
|
|
22
|
+
parseServerMessage;
|
|
23
|
+
options;
|
|
13
24
|
// Connection
|
|
14
25
|
ws = null;
|
|
15
|
-
connectionState =
|
|
16
|
-
url;
|
|
17
|
-
jwtToken;
|
|
18
|
-
abortSignal;
|
|
26
|
+
connectionState = ConnectionState.NEW;
|
|
19
27
|
reconnection;
|
|
20
28
|
// Send management
|
|
21
29
|
sendQueue = new Denque();
|
|
22
30
|
sendCompleted = false;
|
|
31
|
+
onComplete;
|
|
23
32
|
// Response management
|
|
24
33
|
responseQueue = new Denque();
|
|
25
34
|
responseResolvers = [];
|
|
26
35
|
// Error tracking
|
|
27
|
-
|
|
36
|
+
lastError;
|
|
28
37
|
// === Public API ===
|
|
29
38
|
requests = {
|
|
30
39
|
send: async (message) => {
|
|
31
|
-
this.
|
|
32
|
-
return this.enqueueSend(message);
|
|
40
|
+
return await this.enqueueSend(message);
|
|
33
41
|
},
|
|
34
42
|
complete: async () => {
|
|
35
43
|
if (this.sendCompleted)
|
|
36
44
|
return;
|
|
45
|
+
await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream
|
|
46
|
+
try {
|
|
47
|
+
await this.onComplete(this); // custom onComplete may send additional messages
|
|
48
|
+
}
|
|
49
|
+
catch (_) {
|
|
50
|
+
// When 'complete' gets called concurrently with connection break or over a broken
|
|
51
|
+
// transaction stream (server decided it should drop transaction), server would close
|
|
52
|
+
// connection anyway on its end. We can safely ignore error here and just continue working.
|
|
53
|
+
}
|
|
37
54
|
this.sendCompleted = true;
|
|
38
|
-
await this.drainSendQueue();
|
|
39
|
-
this.closeConnection();
|
|
40
55
|
},
|
|
41
56
|
};
|
|
42
57
|
responses = {
|
|
43
58
|
[Symbol.asyncIterator]: () => this.createResponseIterator(),
|
|
44
59
|
};
|
|
45
|
-
|
|
60
|
+
close() {
|
|
61
|
+
this.reconnection.cancel();
|
|
62
|
+
if (this.connectionState < ConnectionState.CONNECTED) {
|
|
63
|
+
// Never reached CONNECTED state. ws.close() will never trigger 'close' event.
|
|
64
|
+
this.ws?.close();
|
|
65
|
+
this.onClose();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (!this.progressConnectionState(ConnectionState.CLOSING))
|
|
69
|
+
return;
|
|
70
|
+
this.ws.close();
|
|
71
|
+
}
|
|
72
|
+
constructor(url, serializeClientMessage, parseServerMessage, options = {}) {
|
|
46
73
|
this.url = url;
|
|
47
|
-
this.
|
|
48
|
-
this.
|
|
74
|
+
this.serializeClientMessage = serializeClientMessage;
|
|
75
|
+
this.parseServerMessage = parseServerMessage;
|
|
76
|
+
this.options = options;
|
|
77
|
+
this.onComplete = this.options.onComplete ?? ((stream) => stream.close());
|
|
78
|
+
const retryConfig = this.options.retryConfig ?? {};
|
|
49
79
|
this.reconnection = new retry_strategy.RetryStrategy(retryConfig, {
|
|
50
80
|
onRetry: () => { void this.connect(); },
|
|
51
81
|
onMaxAttemptsReached: (error) => this.handleError(error),
|
|
52
82
|
});
|
|
53
|
-
if (abortSignal
|
|
54
|
-
this.
|
|
83
|
+
if (this.options.abortSignal?.aborted) {
|
|
84
|
+
this.progressConnectionState(ConnectionState.CLOSED);
|
|
55
85
|
return;
|
|
56
86
|
}
|
|
57
|
-
this.
|
|
58
|
-
|
|
87
|
+
this.options.abortSignal?.addEventListener('abort', () => this.close());
|
|
88
|
+
this.connect();
|
|
59
89
|
}
|
|
60
90
|
// === Connection Lifecycle ===
|
|
61
91
|
connect() {
|
|
62
|
-
if (this.
|
|
92
|
+
if (this.options.abortSignal?.aborted)
|
|
93
|
+
return;
|
|
94
|
+
// Prevent reconnecting after first successful connection.
|
|
95
|
+
if (!this.progressConnectionState(ConnectionState.CONNECTING))
|
|
63
96
|
return;
|
|
64
|
-
this.connectionState = 'connecting';
|
|
65
|
-
this.connectionError = null;
|
|
66
97
|
try {
|
|
67
98
|
this.ws = this.createWebSocket();
|
|
68
|
-
this.
|
|
99
|
+
this.ws.addEventListener('open', () => this.onOpen());
|
|
100
|
+
this.ws.addEventListener('message', (event) => this.onMessage(event.data));
|
|
101
|
+
this.ws.addEventListener('error', (error) => this.onError(error));
|
|
102
|
+
this.ws.addEventListener('close', () => this.onClose());
|
|
69
103
|
}
|
|
70
104
|
catch (error) {
|
|
71
|
-
this.
|
|
72
|
-
this.connectionState = 'disconnected';
|
|
105
|
+
this.lastError = this.toError(error);
|
|
73
106
|
this.reconnection.schedule();
|
|
74
107
|
}
|
|
75
108
|
}
|
|
76
109
|
createWebSocket() {
|
|
77
|
-
const options =
|
|
78
|
-
|
|
79
|
-
:
|
|
110
|
+
const options = {};
|
|
111
|
+
if (this.options.jwtToken)
|
|
112
|
+
options.headers = { authorization: `Bearer ${this.options.jwtToken}` };
|
|
113
|
+
if (this.options.dispatcher)
|
|
114
|
+
options.dispatcher = this.options.dispatcher;
|
|
80
115
|
const ws = new undici.WebSocket(this.url, options);
|
|
81
|
-
|
|
82
|
-
ws.binaryType = 'arraybuffer';
|
|
83
|
-
}
|
|
116
|
+
ws.binaryType = 'arraybuffer';
|
|
84
117
|
return ws;
|
|
85
118
|
}
|
|
86
|
-
attachWebSocketHandlers() {
|
|
87
|
-
if (!this.ws)
|
|
88
|
-
return;
|
|
89
|
-
this.ws.addEventListener('open', () => this.onOpen());
|
|
90
|
-
this.ws.addEventListener('message', (event) => this.onMessage(event.data));
|
|
91
|
-
this.ws.addEventListener('error', (error) => this.onError(error));
|
|
92
|
-
this.ws.addEventListener('close', () => this.onClose());
|
|
93
|
-
}
|
|
94
|
-
attachAbortSignalHandler() {
|
|
95
|
-
this.abortSignal.addEventListener('abort', () => this.close());
|
|
96
|
-
}
|
|
97
119
|
onOpen() {
|
|
98
|
-
this.
|
|
99
|
-
this.
|
|
100
|
-
void this.processSendQueue();
|
|
120
|
+
this.progressConnectionState(ConnectionState.CONNECTED);
|
|
121
|
+
this.processSendQueue();
|
|
101
122
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
123
|
+
onMessage(data) {
|
|
124
|
+
if (!(data instanceof ArrayBuffer)) {
|
|
125
|
+
this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));
|
|
105
126
|
return;
|
|
106
|
-
if (this.sendCompleted) {
|
|
107
|
-
this.finalizeStream();
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
this.connectionState = 'disconnected';
|
|
111
|
-
this.reconnection.schedule();
|
|
112
127
|
}
|
|
113
|
-
}
|
|
114
|
-
onError(error) {
|
|
115
|
-
this.handleError(this.toError(error));
|
|
116
|
-
}
|
|
117
|
-
onMessage(data) {
|
|
118
128
|
try {
|
|
119
|
-
const message = this.
|
|
129
|
+
const message = this.parseServerMessage(new Uint8Array(data));
|
|
120
130
|
this.deliverResponse(message);
|
|
121
131
|
}
|
|
122
132
|
catch (error) {
|
|
123
133
|
this.handleError(this.toError(error));
|
|
124
134
|
}
|
|
125
135
|
}
|
|
126
|
-
|
|
127
|
-
if (this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
close() {
|
|
132
|
-
if (this.isClosed())
|
|
133
|
-
return;
|
|
134
|
-
this.connectionState = 'closed';
|
|
135
|
-
this.reconnection.cancel();
|
|
136
|
-
this.closeWebSocket();
|
|
137
|
-
this.rejectAllPendingOperations();
|
|
138
|
-
}
|
|
139
|
-
closeWebSocket() {
|
|
140
|
-
if (!this.ws)
|
|
136
|
+
onError(error) {
|
|
137
|
+
if (this.connectionState < ConnectionState.CONNECTED) {
|
|
138
|
+
// Try to connect several times until we succeed or run out of attempts.
|
|
139
|
+
this.lastError = this.toError(error);
|
|
140
|
+
this.reconnection.schedule();
|
|
141
141
|
return;
|
|
142
|
-
try {
|
|
143
|
-
this.ws.close();
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
// Suppress close errors
|
|
147
142
|
}
|
|
148
|
-
this.
|
|
149
|
-
}
|
|
150
|
-
finalizeStream() {
|
|
151
|
-
this.connectionState = 'closed';
|
|
152
|
-
this.resolveAllPendingResponses();
|
|
143
|
+
this.handleError(this.toError(error));
|
|
153
144
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
145
|
+
onClose() {
|
|
146
|
+
this.progressConnectionState(ConnectionState.CLOSED);
|
|
147
|
+
if (!this.lastError) {
|
|
148
|
+
this.rejectAllSendOperations(this.createStreamClosedError());
|
|
149
|
+
this.resolveAllPendingResponses(); // unblock active async iterator
|
|
158
150
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (data instanceof ArrayBuffer) {
|
|
162
|
-
return api.TxAPI_ServerMessage.fromBinary(new Uint8Array(data));
|
|
151
|
+
else {
|
|
152
|
+
this.rejectAllPendingOperations(this.lastError);
|
|
163
153
|
}
|
|
164
|
-
throw new Error(`Unsupported message format: ${typeof data}`);
|
|
165
154
|
}
|
|
166
155
|
// === Send Queue Management ===
|
|
167
|
-
|
|
156
|
+
enqueueSend(message) {
|
|
168
157
|
if (this.sendCompleted) {
|
|
169
158
|
throw new Error('Cannot send: stream already completed');
|
|
170
159
|
}
|
|
171
|
-
if (this.abortSignal
|
|
160
|
+
if (this.options.abortSignal?.aborted) {
|
|
172
161
|
throw new Error('Cannot send: stream aborted');
|
|
173
162
|
}
|
|
174
|
-
}
|
|
175
|
-
enqueueSend(message) {
|
|
176
163
|
return new Promise((resolve, reject) => {
|
|
177
164
|
this.sendQueue.push({ message, resolve, reject });
|
|
178
|
-
|
|
165
|
+
this.processSendQueue();
|
|
179
166
|
});
|
|
180
167
|
}
|
|
181
168
|
processSendQueue() {
|
|
@@ -187,7 +174,7 @@ class WebSocketBiDiStream {
|
|
|
187
174
|
}
|
|
188
175
|
}
|
|
189
176
|
canSendMessages() {
|
|
190
|
-
return this.connectionState ===
|
|
177
|
+
return this.connectionState === ConnectionState.CONNECTED;
|
|
191
178
|
}
|
|
192
179
|
sendQueuedMessage(queued) {
|
|
193
180
|
try {
|
|
@@ -199,7 +186,7 @@ class WebSocketBiDiStream {
|
|
|
199
186
|
if (ws.readyState !== undici.WebSocket.OPEN) {
|
|
200
187
|
throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);
|
|
201
188
|
}
|
|
202
|
-
const binary =
|
|
189
|
+
const binary = this.serializeClientMessage(queued.message);
|
|
203
190
|
ws.send(binary);
|
|
204
191
|
queued.resolve();
|
|
205
192
|
}
|
|
@@ -208,25 +195,25 @@ class WebSocketBiDiStream {
|
|
|
208
195
|
}
|
|
209
196
|
}
|
|
210
197
|
async drainSendQueue() {
|
|
211
|
-
const POLL_INTERVAL_MS =
|
|
198
|
+
const POLL_INTERVAL_MS = 5;
|
|
212
199
|
while (this.sendQueue.length > 0) {
|
|
213
200
|
await this.waitForCondition(() => this.sendQueue.length === 0, POLL_INTERVAL_MS);
|
|
214
201
|
}
|
|
215
202
|
}
|
|
216
203
|
waitForCondition(condition, intervalMs) {
|
|
217
204
|
return new Promise((resolve, reject) => {
|
|
218
|
-
if (this.abortSignal
|
|
219
|
-
return reject(this.toError(this.abortSignal.reason) ?? new Error('Stream aborted'));
|
|
205
|
+
if (this.options.abortSignal?.aborted) {
|
|
206
|
+
return reject(this.toError(this.options.abortSignal.reason) ?? new Error('Stream aborted'));
|
|
220
207
|
}
|
|
221
208
|
let timeoutId;
|
|
222
209
|
const onAbort = () => {
|
|
223
210
|
clearTimeout(timeoutId);
|
|
224
|
-
reject(this.toError(this.abortSignal
|
|
211
|
+
reject(this.toError(this.options.abortSignal?.reason) ?? new Error('Stream aborted'));
|
|
225
212
|
};
|
|
226
|
-
this.abortSignal
|
|
213
|
+
this.options.abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
227
214
|
const check = () => {
|
|
228
215
|
if (condition() || this.isStreamEnded()) {
|
|
229
|
-
this.abortSignal
|
|
216
|
+
this.options.abortSignal?.removeEventListener('abort', onAbort);
|
|
230
217
|
resolve();
|
|
231
218
|
}
|
|
232
219
|
else {
|
|
@@ -264,8 +251,8 @@ class WebSocketBiDiStream {
|
|
|
264
251
|
}
|
|
265
252
|
// Stream ended
|
|
266
253
|
if (this.isStreamEnded()) {
|
|
267
|
-
if (this.
|
|
268
|
-
reject(this.
|
|
254
|
+
if (this.lastError) {
|
|
255
|
+
reject(this.lastError);
|
|
269
256
|
}
|
|
270
257
|
else {
|
|
271
258
|
resolve({ value: undefined, done: true });
|
|
@@ -276,20 +263,20 @@ class WebSocketBiDiStream {
|
|
|
276
263
|
this.responseResolvers.push({ resolve, reject });
|
|
277
264
|
});
|
|
278
265
|
}
|
|
266
|
+
resolveAllPendingResponses() {
|
|
267
|
+
while (this.responseResolvers.length > 0) {
|
|
268
|
+
const resolver = this.responseResolvers.shift();
|
|
269
|
+
resolver.resolve({ value: undefined, done: true });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
279
272
|
// === Error Handling ===
|
|
280
273
|
handleError(error) {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
this.connectionState = 'closed';
|
|
284
|
-
this.connectionError = error;
|
|
285
|
-
this.reconnection.cancel();
|
|
286
|
-
this.closeWebSocket();
|
|
287
|
-
this.rejectAllPendingOperations(error);
|
|
274
|
+
this.lastError = error;
|
|
275
|
+
this.close();
|
|
288
276
|
}
|
|
289
277
|
rejectAllPendingOperations(error) {
|
|
290
|
-
|
|
291
|
-
this.
|
|
292
|
-
this.rejectAllResponseResolvers(err);
|
|
278
|
+
this.rejectAllSendOperations(error);
|
|
279
|
+
this.rejectAllResponseResolvers(error);
|
|
293
280
|
}
|
|
294
281
|
rejectAllSendOperations(error) {
|
|
295
282
|
while (this.sendQueue.length > 0) {
|
|
@@ -304,8 +291,8 @@ class WebSocketBiDiStream {
|
|
|
304
291
|
}
|
|
305
292
|
}
|
|
306
293
|
createStreamClosedError() {
|
|
307
|
-
if (this.abortSignal
|
|
308
|
-
const reason = this.abortSignal.reason;
|
|
294
|
+
if (this.options.abortSignal?.aborted) {
|
|
295
|
+
const reason = this.options.abortSignal.reason;
|
|
309
296
|
if (reason instanceof Error) {
|
|
310
297
|
return reason;
|
|
311
298
|
}
|
|
@@ -313,19 +300,29 @@ class WebSocketBiDiStream {
|
|
|
313
300
|
}
|
|
314
301
|
return new Error('Stream closed');
|
|
315
302
|
}
|
|
316
|
-
// ===
|
|
317
|
-
isConnectingOrConnected() {
|
|
318
|
-
return this.connectionState === 'connecting'
|
|
319
|
-
|| this.connectionState === 'connected';
|
|
320
|
-
}
|
|
321
|
-
isClosed() {
|
|
322
|
-
return this.connectionState === 'closed';
|
|
323
|
-
}
|
|
303
|
+
// === Helpers ===
|
|
324
304
|
isStreamEnded() {
|
|
325
|
-
return this.
|
|
305
|
+
return this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false;
|
|
326
306
|
}
|
|
327
307
|
toError(error) {
|
|
328
|
-
|
|
308
|
+
if (error instanceof Error)
|
|
309
|
+
return error;
|
|
310
|
+
if (error instanceof undici.ErrorEvent)
|
|
311
|
+
return error.error;
|
|
312
|
+
return new Error(String(error));
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Connection state progresses linearly from NEW to CLOSED and never goes back.
|
|
316
|
+
* This internal contract dramatically simplifies the internal stream state management.
|
|
317
|
+
*
|
|
318
|
+
* If you ever feel the need to make this contract less strict, think twice.
|
|
319
|
+
*/
|
|
320
|
+
progressConnectionState(newState) {
|
|
321
|
+
if (newState < this.connectionState) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
this.connectionState = newState;
|
|
325
|
+
return true;
|
|
329
326
|
}
|
|
330
327
|
}
|
|
331
328
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket_stream.cjs","sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket } from 'undici';\nimport {\n TxAPI_ClientMessage as ClientMessageType,\n TxAPI_ServerMessage as ServerMessageType,\n} from '../proto-grpc/github.com/milaboratory/pl/plapi/plapiproto/api';\nimport type { BiDiStream } from './abstract_stream';\nimport Denque from 'denque';\nimport type { RetryConfig } from '../helpers/retry_strategy';\nimport { RetryStrategy } from '../helpers/retry_strategy';\n\ntype ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'closing' | 'closed';\n\ninterface QueuedMessage {\n message: ClientMessageType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver {\n resolve: (value: IteratorResult<ServerMessageType>) => void;\n reject: (error: Error) => void;\n}\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream implements BiDiStream<ClientMessageType, ServerMessageType> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = 'disconnected';\n private readonly url: string;\n private readonly jwtToken?: string;\n private readonly abortSignal: AbortSignal;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage>();\n private sendCompleted = false;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMessageType>();\n private responseResolvers: ResponseResolver[] = [];\n\n // Error tracking\n private connectionError: Error | null = null;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMessageType): Promise<void> => {\n this.validateSendState();\n return this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n this.sendCompleted = true;\n await this.drainSendQueue();\n this.closeConnection();\n },\n };\n\n public readonly responses: AsyncIterable<ServerMessageType> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n constructor(\n url: string,\n abortSignal: AbortSignal,\n jwtToken?: string,\n retryConfig: Partial<RetryConfig> = {},\n ) {\n this.url = url;\n this.jwtToken = jwtToken;\n this.abortSignal = abortSignal;\n\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => { void this.connect(); },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (abortSignal.aborted) {\n this.connectionState = 'closed';\n return;\n }\n\n this.attachAbortSignalHandler();\n void this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.isConnectingOrConnected() || this.abortSignal.aborted) return;\n\n this.connectionState = 'connecting';\n this.connectionError = null;\n\n try {\n this.ws = this.createWebSocket();\n this.attachWebSocketHandlers();\n } catch (error) {\n this.connectionError = this.toError(error);\n this.connectionState = 'disconnected';\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options = this.jwtToken\n ? { headers: { authorization: `Bearer ${this.jwtToken}` } }\n : undefined;\n\n const ws = new (WebSocket as any)(this.url, options);\n if (ws) {\n ws.binaryType = 'arraybuffer';\n }\n return ws;\n }\n\n private attachWebSocketHandlers(): void {\n if (!this.ws) return;\n\n this.ws.addEventListener('open', () => this.onOpen());\n this.ws.addEventListener('message', (event) => this.onMessage(event.data));\n this.ws.addEventListener('error', (error) => this.onError(error));\n this.ws.addEventListener('close', () => this.onClose());\n }\n\n private attachAbortSignalHandler(): void {\n this.abortSignal.addEventListener('abort', () => this.close());\n }\n\n private onOpen(): void {\n this.connectionState = 'connected';\n this.reconnection.reset();\n void this.processSendQueue();\n }\n\n private onClose(): void {\n this.ws = null;\n\n if (this.isClosed() || this.abortSignal.aborted) return;\n\n if (this.sendCompleted) {\n this.finalizeStream();\n } else {\n this.connectionState = 'disconnected';\n this.reconnection.schedule();\n }\n }\n\n private onError(error: unknown): void {\n this.handleError(this.toError(error));\n }\n\n private onMessage(data: unknown): void {\n try {\n const message = this.parseMessage(data);\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private closeConnection(): void {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.close();\n }\n }\n\n private close(): void {\n if (this.isClosed()) return;\n\n this.connectionState = 'closed';\n this.reconnection.cancel();\n this.closeWebSocket();\n this.rejectAllPendingOperations();\n }\n\n private closeWebSocket(): void {\n if (!this.ws) return;\n\n try {\n this.ws.close();\n } catch {\n // Suppress close errors\n }\n\n this.ws = null;\n }\n\n private finalizeStream(): void {\n this.connectionState = 'closed';\n this.resolveAllPendingResponses();\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n private parseMessage(data: unknown): ServerMessageType {\n if (data instanceof ArrayBuffer) {\n return ServerMessageType.fromBinary(new Uint8Array(data));\n }\n\n throw new Error(`Unsupported message format: ${typeof data}`);\n }\n\n // === Send Queue Management ===\n\n private validateSendState(): void {\n if (this.sendCompleted) {\n throw new Error('Cannot send: stream already completed');\n }\n\n if (this.abortSignal.aborted) {\n throw new Error('Cannot send: stream aborted');\n }\n }\n\n private enqueueSend(message: ClientMessageType): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n void this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === 'connected' && this.ws !== null;\n }\n\n private sendQueuedMessage(queued: QueuedMessage): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error('WebSocket is not connected');\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = ClientMessageType.toBinary(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 10;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(\n () => this.sendQueue.length === 0,\n POLL_INTERVAL_MS,\n );\n }\n }\n\n private waitForCondition(\n condition: () => boolean,\n intervalMs: number,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.abortSignal.aborted) {\n return reject(this.toError(this.abortSignal.reason) ?? new Error('Stream aborted'));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.abortSignal.reason) ?? new Error('Stream aborted'));\n };\n\n this.abortSignal.addEventListener('abort', onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.abortSignal.removeEventListener('abort', onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMessageType): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMessageType> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMessageType>> {\n return new Promise<IteratorResult<ServerMessageType>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.connectionError) {\n reject(this.connectionError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n // === Error Handling ===\n private handleError(error: Error): void {\n if (this.isClosed()) return;\n\n this.connectionState = 'closed';\n this.connectionError = error;\n this.reconnection.cancel();\n this.closeWebSocket();\n this.rejectAllPendingOperations(error);\n }\n\n private rejectAllPendingOperations(error?: Error): void {\n const err = error ?? this.createStreamClosedError();\n this.rejectAllSendOperations(err);\n this.rejectAllResponseResolvers(err);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.abortSignal.aborted) {\n const reason = this.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error('Stream aborted', { cause: reason });\n }\n return new Error('Stream closed');\n }\n // === State Checks ===\n\n private isConnectingOrConnected(): boolean {\n return this.connectionState === 'connecting'\n || this.connectionState === 'connected';\n }\n\n private isClosed(): boolean {\n return this.connectionState === 'closed';\n }\n\n private isStreamEnded(): boolean {\n return this.isClosed() || this.abortSignal.aborted;\n }\n\n private toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n }\n}\n"],"names":["RetryStrategy","WebSocket","ServerMessageType","ClientMessageType"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"websocket_stream.cjs","sources":["../../src/core/websocket_stream.ts"],"sourcesContent":["import { WebSocket, type WebSocketInit, type Dispatcher, ErrorEvent } from 'undici';\nimport type { BiDiStream } from './abstract_stream';\nimport Denque from 'denque';\nimport type { RetryConfig } from '../helpers/retry_strategy';\nimport { RetryStrategy } from '../helpers/retry_strategy';\n\ninterface QueuedMessage<InType extends object> {\n message: InType;\n resolve: () => void;\n reject: (error: Error) => void;\n}\n\ninterface ResponseResolver<OutType extends object> {\n resolve: (value: IteratorResult<OutType>) => void;\n reject: (error: Error) => void;\n}\n\nenum ConnectionState {\n NEW = 0,\n CONNECTING = 1,\n CONNECTED = 2,\n CLOSING = 3,\n CLOSED = 4,\n}\n\nexport type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {\n abortSignal?: AbortSignal;\n\n dispatcher?: Dispatcher;\n jwtToken?: string;\n retryConfig?: Partial<RetryConfig>;\n\n onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n};\n\n/**\n * WebSocket-based bidirectional stream implementation for LLTransaction.\n * Implements BiDiStream interface which is compatible with DuplexStreamingCall.\n */\nexport class WebSocketBiDiStream<ClientMsg extends object, ServerMsg extends object> implements BiDiStream<ClientMsg, ServerMsg> {\n // Connection\n private ws: WebSocket | null = null;\n private connectionState: ConnectionState = ConnectionState.NEW;\n private readonly reconnection: RetryStrategy;\n\n // Send management\n private readonly sendQueue = new Denque<QueuedMessage<ClientMsg>>();\n private sendCompleted = false;\n private readonly onComplete: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;\n\n // Response management\n private readonly responseQueue = new Denque<ServerMsg>();\n private responseResolvers: ResponseResolver<ServerMsg>[] = [];\n\n // Error tracking\n private lastError?: Error;\n\n // === Public API ===\n\n public readonly requests = {\n send: async (message: ClientMsg): Promise<void> => {\n return await this.enqueueSend(message);\n },\n\n complete: async (): Promise<void> => {\n if (this.sendCompleted) return;\n\n await this.drainSendQueue(); // ensure we sent all already queued messages before closing the stream\n try {\n await this.onComplete(this); // custom onComplete may send additional messages\n } catch (_: unknown) {\n // When 'complete' gets called concurrently with connection break or over a broken\n // transaction stream (server decided it should drop transaction), server would close\n // connection anyway on its end. We can safely ignore error here and just continue working.\n }\n this.sendCompleted = true;\n },\n };\n\n public readonly responses: AsyncIterable<ServerMsg> = {\n [Symbol.asyncIterator]: () => this.createResponseIterator(),\n };\n\n public close(): void {\n this.reconnection.cancel();\n\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Never reached CONNECTED state. ws.close() will never trigger 'close' event.\n this.ws?.close();\n this.onClose();\n return;\n }\n\n if (!this.progressConnectionState(ConnectionState.CLOSING)) return;\n this.ws!.close();\n }\n\n constructor(\n private readonly url: string,\n private readonly serializeClientMessage: (message: ClientMsg) => Uint8Array,\n private readonly parseServerMessage: (data: Uint8Array) => ServerMsg,\n private readonly options: WSStreamOptions<ClientMsg, ServerMsg> = {},\n ) {\n this.onComplete = this.options.onComplete ?? ((stream) => stream.close());\n\n const retryConfig = this.options.retryConfig ?? {};\n this.reconnection = new RetryStrategy(retryConfig, {\n onRetry: () => { void this.connect(); },\n onMaxAttemptsReached: (error) => this.handleError(error),\n });\n\n if (this.options.abortSignal?.aborted) {\n this.progressConnectionState(ConnectionState.CLOSED);\n return;\n }\n\n this.options.abortSignal?.addEventListener('abort', () => this.close());\n this.connect();\n }\n\n // === Connection Lifecycle ===\n\n private connect(): void {\n if (this.options.abortSignal?.aborted) return;\n\n // Prevent reconnecting after first successful connection.\n if (!this.progressConnectionState(ConnectionState.CONNECTING)) return;\n\n try {\n this.ws = this.createWebSocket();\n\n this.ws.addEventListener('open', () => this.onOpen());\n this.ws.addEventListener('message', (event) => this.onMessage(event.data));\n this.ws.addEventListener('error', (error) => this.onError(error));\n this.ws.addEventListener('close', () => this.onClose());\n } catch (error) {\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n }\n }\n\n private createWebSocket(): WebSocket {\n const options: WebSocketInit = {};\n\n if (this.options.jwtToken) options.headers = { authorization: `Bearer ${this.options.jwtToken}` };\n if (this.options.dispatcher) options.dispatcher = this.options.dispatcher;\n\n const ws = new WebSocket(this.url, options);\n ws.binaryType = 'arraybuffer';\n return ws;\n }\n\n private onOpen(): void {\n this.progressConnectionState(ConnectionState.CONNECTED);\n this.processSendQueue();\n }\n\n private onMessage(data: unknown): void {\n if (!(data instanceof ArrayBuffer)) {\n this.handleError(new Error(`Unexpected WS message format: ${typeof data}`));\n return;\n }\n\n try {\n const message = this.parseServerMessage(new Uint8Array(data));\n this.deliverResponse(message);\n } catch (error) {\n this.handleError(this.toError(error));\n }\n }\n\n private onError(error: unknown): void {\n if (this.connectionState < ConnectionState.CONNECTED) {\n // Try to connect several times until we succeed or run out of attempts.\n this.lastError = this.toError(error);\n this.reconnection.schedule();\n return;\n }\n\n this.handleError(this.toError(error));\n }\n\n private onClose(): void {\n this.progressConnectionState(ConnectionState.CLOSED);\n\n if (!this.lastError) {\n this.rejectAllSendOperations(this.createStreamClosedError());\n this.resolveAllPendingResponses(); // unblock active async iterator\n } else {\n this.rejectAllPendingOperations(this.lastError);\n }\n }\n\n // === Send Queue Management ===\n\n private enqueueSend(message: ClientMsg): Promise<void> {\n if (this.sendCompleted) {\n throw new Error('Cannot send: stream already completed');\n }\n\n if (this.options.abortSignal?.aborted) {\n throw new Error('Cannot send: stream aborted');\n }\n\n return new Promise<void>((resolve, reject) => {\n this.sendQueue.push({ message, resolve, reject });\n this.processSendQueue();\n });\n }\n\n private processSendQueue(): void {\n if (!this.canSendMessages()) return;\n\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n this.sendQueuedMessage(queued);\n }\n }\n\n private canSendMessages(): boolean {\n return this.connectionState === ConnectionState.CONNECTED;\n }\n\n private sendQueuedMessage(queued: QueuedMessage<ClientMsg>): void {\n try {\n const ws = this.ws;\n if (!ws) {\n throw new Error('WebSocket is not connected');\n }\n\n // Check if WebSocket is in a valid state for sending\n if (ws.readyState !== WebSocket.OPEN) {\n throw new Error(`WebSocket is not open (readyState: ${ws.readyState})`);\n }\n\n const binary = this.serializeClientMessage(queued.message);\n ws.send(binary);\n queued.resolve();\n } catch (error) {\n queued.reject(this.toError(error));\n }\n }\n\n private async drainSendQueue(): Promise<void> {\n const POLL_INTERVAL_MS = 5;\n\n while (this.sendQueue.length > 0) {\n await this.waitForCondition(\n () => this.sendQueue.length === 0,\n POLL_INTERVAL_MS,\n );\n }\n }\n\n private waitForCondition(\n condition: () => boolean,\n intervalMs: number,\n ): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (this.options.abortSignal?.aborted) {\n return reject(this.toError(this.options.abortSignal.reason) ?? new Error('Stream aborted'));\n }\n\n let timeoutId: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(this.toError(this.options.abortSignal?.reason) ?? new Error('Stream aborted'));\n };\n\n this.options.abortSignal?.addEventListener('abort', onAbort, { once: true });\n\n const check = () => {\n if (condition() || this.isStreamEnded()) {\n this.options.abortSignal?.removeEventListener('abort', onAbort);\n resolve();\n } else {\n timeoutId = setTimeout(check, intervalMs);\n }\n };\n\n check();\n });\n }\n\n // === Response Delivery ===\n\n private deliverResponse(message: ServerMsg): void {\n if (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: message, done: false });\n } else {\n this.responseQueue.push(message);\n }\n }\n\n private async *createResponseIterator(): AsyncIterator<ServerMsg> {\n while (true) {\n const result = await this.nextResponse();\n\n if (result.done) break;\n\n yield result.value;\n }\n }\n\n private nextResponse(): Promise<IteratorResult<ServerMsg>> {\n return new Promise<IteratorResult<ServerMsg>>((resolve, reject) => {\n // Fast path: message already available\n if (this.responseQueue.length > 0) {\n const message = this.responseQueue.shift()!;\n resolve({ value: message, done: false });\n return;\n }\n\n // Stream ended\n if (this.isStreamEnded()) {\n if (this.lastError) {\n reject(this.lastError);\n } else {\n resolve({ value: undefined as any, done: true });\n }\n return;\n }\n\n // Wait for next message\n this.responseResolvers.push({ resolve, reject });\n });\n }\n\n private resolveAllPendingResponses(): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.resolve({ value: undefined as any, done: true });\n }\n }\n\n // === Error Handling ===\n\n private handleError(error: Error): void {\n this.lastError = error;\n this.close();\n }\n\n private rejectAllPendingOperations(error: Error): void {\n this.rejectAllSendOperations(error);\n this.rejectAllResponseResolvers(error);\n }\n\n private rejectAllSendOperations(error: Error): void {\n while (this.sendQueue.length > 0) {\n const queued = this.sendQueue.shift()!;\n queued.reject(error);\n }\n }\n\n private rejectAllResponseResolvers(error: Error): void {\n while (this.responseResolvers.length > 0) {\n const resolver = this.responseResolvers.shift()!;\n resolver.reject(error);\n }\n }\n\n private createStreamClosedError(): Error {\n if (this.options.abortSignal?.aborted) {\n const reason = this.options.abortSignal.reason;\n if (reason instanceof Error) {\n return reason;\n }\n return new Error('Stream aborted', { cause: reason });\n }\n\n return new Error('Stream closed');\n }\n\n // === Helpers ===\n\n private isStreamEnded(): boolean {\n return this.connectionState === ConnectionState.CLOSED || this.options.abortSignal?.aborted || false;\n }\n\n private toError(error: unknown): Error {\n if (error instanceof Error) return error;\n if (error instanceof ErrorEvent) return error.error;\n return new Error(String(error));\n }\n\n /**\n * Connection state progresses linearly from NEW to CLOSED and never goes back.\n * This internal contract dramatically simplifies the internal stream state management.\n *\n * If you ever feel the need to make this contract less strict, think twice.\n */\n private progressConnectionState(newState: ConnectionState): boolean {\n if (newState < this.connectionState) {\n return false;\n }\n this.connectionState = newState;\n return true;\n }\n}\n"],"names":["RetryStrategy","WebSocket","ErrorEvent"],"mappings":"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"}
|
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Dispatcher } from 'undici';
|
|
2
2
|
import type { BiDiStream } from './abstract_stream';
|
|
3
3
|
import type { RetryConfig } from '../helpers/retry_strategy';
|
|
4
|
+
export type WSStreamOptions<ClientMsg extends object, ServerMsg extends object> = {
|
|
5
|
+
abortSignal?: AbortSignal;
|
|
6
|
+
dispatcher?: Dispatcher;
|
|
7
|
+
jwtToken?: string;
|
|
8
|
+
retryConfig?: Partial<RetryConfig>;
|
|
9
|
+
onComplete?: (stream: WebSocketBiDiStream<ClientMsg, ServerMsg>) => void | Promise<void>;
|
|
10
|
+
};
|
|
4
11
|
/**
|
|
5
12
|
* WebSocket-based bidirectional stream implementation for LLTransaction.
|
|
6
13
|
* Implements BiDiStream interface which is compatible with DuplexStreamingCall.
|
|
7
14
|
*/
|
|
8
|
-
export declare class WebSocketBiDiStream implements BiDiStream<
|
|
15
|
+
export declare class WebSocketBiDiStream<ClientMsg extends object, ServerMsg extends object> implements BiDiStream<ClientMsg, ServerMsg> {
|
|
16
|
+
private readonly url;
|
|
17
|
+
private readonly serializeClientMessage;
|
|
18
|
+
private readonly parseServerMessage;
|
|
19
|
+
private readonly options;
|
|
9
20
|
private ws;
|
|
10
21
|
private connectionState;
|
|
11
|
-
private readonly url;
|
|
12
|
-
private readonly jwtToken?;
|
|
13
|
-
private readonly abortSignal;
|
|
14
22
|
private readonly reconnection;
|
|
15
23
|
private readonly sendQueue;
|
|
16
24
|
private sendCompleted;
|
|
25
|
+
private readonly onComplete;
|
|
17
26
|
private readonly responseQueue;
|
|
18
27
|
private responseResolvers;
|
|
19
|
-
private
|
|
28
|
+
private lastError?;
|
|
20
29
|
readonly requests: {
|
|
21
|
-
send: (message:
|
|
30
|
+
send: (message: ClientMsg) => Promise<void>;
|
|
22
31
|
complete: () => Promise<void>;
|
|
23
32
|
};
|
|
24
|
-
readonly responses: AsyncIterable<
|
|
25
|
-
|
|
33
|
+
readonly responses: AsyncIterable<ServerMsg>;
|
|
34
|
+
close(): void;
|
|
35
|
+
constructor(url: string, serializeClientMessage: (message: ClientMsg) => Uint8Array, parseServerMessage: (data: Uint8Array) => ServerMsg, options?: WSStreamOptions<ClientMsg, ServerMsg>);
|
|
26
36
|
private connect;
|
|
27
37
|
private createWebSocket;
|
|
28
|
-
private attachWebSocketHandlers;
|
|
29
|
-
private attachAbortSignalHandler;
|
|
30
38
|
private onOpen;
|
|
31
|
-
private onClose;
|
|
32
|
-
private onError;
|
|
33
39
|
private onMessage;
|
|
34
|
-
private
|
|
35
|
-
private
|
|
36
|
-
private closeWebSocket;
|
|
37
|
-
private finalizeStream;
|
|
38
|
-
private resolveAllPendingResponses;
|
|
39
|
-
private parseMessage;
|
|
40
|
-
private validateSendState;
|
|
40
|
+
private onError;
|
|
41
|
+
private onClose;
|
|
41
42
|
private enqueueSend;
|
|
42
43
|
private processSendQueue;
|
|
43
44
|
private canSendMessages;
|
|
@@ -47,14 +48,20 @@ export declare class WebSocketBiDiStream implements BiDiStream<ClientMessageType
|
|
|
47
48
|
private deliverResponse;
|
|
48
49
|
private createResponseIterator;
|
|
49
50
|
private nextResponse;
|
|
51
|
+
private resolveAllPendingResponses;
|
|
50
52
|
private handleError;
|
|
51
53
|
private rejectAllPendingOperations;
|
|
52
54
|
private rejectAllSendOperations;
|
|
53
55
|
private rejectAllResponseResolvers;
|
|
54
56
|
private createStreamClosedError;
|
|
55
|
-
private isConnectingOrConnected;
|
|
56
|
-
private isClosed;
|
|
57
57
|
private isStreamEnded;
|
|
58
58
|
private toError;
|
|
59
|
+
/**
|
|
60
|
+
* Connection state progresses linearly from NEW to CLOSED and never goes back.
|
|
61
|
+
* This internal contract dramatically simplifies the internal stream state management.
|
|
62
|
+
*
|
|
63
|
+
* If you ever feel the need to make this contract less strict, think twice.
|
|
64
|
+
*/
|
|
65
|
+
private progressConnectionState;
|
|
59
66
|
}
|
|
60
67
|
//# sourceMappingURL=websocket_stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket_stream.d.ts","sourceRoot":"","sources":["../../src/core/websocket_stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"websocket_stream.d.ts","sourceRoot":"","sources":["../../src/core/websocket_stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,KAAK,UAAU,EAAc,MAAM,QAAQ,CAAC;AACpF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAsB7D,MAAM,MAAM,eAAe,CAAC,SAAS,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,IAAI;IAChF,WAAW,CAAC,EAAE,WAAW,CAAC;IAE1B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAEnC,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1F,CAAC;AAEF;;;GAGG;AACH,qBAAa,mBAAmB,CAAC,SAAS,SAAS,MAAM,EAAE,SAAS,SAAS,MAAM,CAAE,YAAW,UAAU,CAAC,SAAS,EAAE,SAAS,CAAC;IA2D5H,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,sBAAsB;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IA5D1B,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,eAAe,CAAwC;IAC/D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgB;IAG7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IACpE,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA8E;IAGzG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;IACzD,OAAO,CAAC,iBAAiB,CAAqC;IAG9D,OAAO,CAAC,SAAS,CAAC,CAAQ;IAI1B,SAAgB,QAAQ;wBACA,SAAS,KAAG,OAAO,CAAC,IAAI,CAAC;wBAI3B,OAAO,CAAC,IAAI,CAAC;MAajC;IAEF,SAAgB,SAAS,EAAE,aAAa,CAAC,SAAS,CAAC,CAEjD;IAEK,KAAK,IAAI,IAAI;gBAeD,GAAG,EAAE,MAAM,EACX,sBAAsB,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,UAAU,EAC1D,kBAAkB,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,SAAS,EACnD,OAAO,GAAE,eAAe,CAAC,SAAS,EAAE,SAAS,CAAM;IAqBtE,OAAO,CAAC,OAAO;IAmBf,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,MAAM;IAKd,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,OAAO;IAaf,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,iBAAiB;YAoBX,cAAc;IAW5B,OAAO,CAAC,gBAAgB;IAgCxB,OAAO,CAAC,eAAe;YASR,sBAAsB;IAUrC,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,0BAA0B;IASlC,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,0BAA0B;IAKlC,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,0BAA0B;IAOlC,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,OAAO;IAMf;;;;;OAKG;IACH,OAAO,CAAC,uBAAuB;CAOhC"}
|