@api.global/typedsocket 4.1.4 → 4.2.1
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/typedsocket.classes.typedsocket.d.ts +13 -0
- package/dist_ts/typedsocket.classes.typedsocket.js +158 -19
- package/package.json +13 -13
- package/readme.md +48 -13
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/typedsocket.classes.typedsocket.ts +171 -22
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@api.global/typedsocket',
|
|
6
|
-
version: '4.1
|
|
6
|
+
version: '4.2.1',
|
|
7
7
|
description: 'A library for creating typed WebSocket connections, supporting bi-directional communication with type safety.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSx5QkFBeUI7SUFDL0IsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLCtHQUErRztDQUM3SCxDQUFBIn0=
|
|
@@ -9,6 +9,7 @@ export interface ITypedSocketClientOptions {
|
|
|
9
9
|
maxRetries?: number;
|
|
10
10
|
initialBackoffMs?: number;
|
|
11
11
|
maxBackoffMs?: number;
|
|
12
|
+
abortSignal?: AbortSignal;
|
|
12
13
|
}
|
|
13
14
|
/**
|
|
14
15
|
* Wrapper for SmartServe's IWebSocketPeer to provide tag compatibility
|
|
@@ -80,7 +81,11 @@ export declare class TypedSocket {
|
|
|
80
81
|
private serverUrl;
|
|
81
82
|
private retryCount;
|
|
82
83
|
private currentBackoff;
|
|
84
|
+
private stopped;
|
|
85
|
+
private abortSignalListener?;
|
|
86
|
+
private stopListeners;
|
|
83
87
|
private pendingRequests;
|
|
88
|
+
private clientTags;
|
|
84
89
|
private smartServeRef;
|
|
85
90
|
private smartServeConnectionWrappers;
|
|
86
91
|
private constructor();
|
|
@@ -104,6 +109,9 @@ export declare class TypedSocket {
|
|
|
104
109
|
* Schedules a reconnection attempt with exponential backoff
|
|
105
110
|
*/
|
|
106
111
|
private scheduleReconnect;
|
|
112
|
+
private shouldReconnect;
|
|
113
|
+
private waitForReconnectDelay;
|
|
114
|
+
private createAbortError;
|
|
107
115
|
/**
|
|
108
116
|
* Updates connection status and notifies subscribers
|
|
109
117
|
*/
|
|
@@ -132,6 +140,11 @@ export declare class TypedSocket {
|
|
|
132
140
|
* @client-only
|
|
133
141
|
*/
|
|
134
142
|
setTag<T extends plugins.typedrequestInterfaces.ITag>(name: T['name'], payload: T['payload']): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Re-applies all client tags after a reconnect, since server-side tags are
|
|
145
|
+
* bound to the previous connection.
|
|
146
|
+
*/
|
|
147
|
+
private reapplyClientTags;
|
|
135
148
|
/**
|
|
136
149
|
* Removes a tag from this client connection.
|
|
137
150
|
* @client-only
|
|
@@ -49,7 +49,22 @@ export class TypedSocket {
|
|
|
49
49
|
typedSocket.clientOptions = opts;
|
|
50
50
|
typedSocket.serverUrl = serverUrlArg;
|
|
51
51
|
typedSocket.currentBackoff = opts.initialBackoffMs;
|
|
52
|
-
|
|
52
|
+
if (opts.abortSignal?.aborted) {
|
|
53
|
+
throw typedSocket.createAbortError();
|
|
54
|
+
}
|
|
55
|
+
if (opts.abortSignal) {
|
|
56
|
+
typedSocket.abortSignalListener = () => {
|
|
57
|
+
void typedSocket.stop();
|
|
58
|
+
};
|
|
59
|
+
opts.abortSignal.addEventListener('abort', typedSocket.abortSignalListener, { once: true });
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
await typedSocket.connect();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
await typedSocket.stop();
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
53
68
|
return typedSocket;
|
|
54
69
|
}
|
|
55
70
|
/**
|
|
@@ -129,7 +144,12 @@ export class TypedSocket {
|
|
|
129
144
|
this.serverUrl = '';
|
|
130
145
|
this.retryCount = 0;
|
|
131
146
|
this.currentBackoff = 1000;
|
|
147
|
+
this.stopped = false;
|
|
148
|
+
this.stopListeners = new Set();
|
|
132
149
|
this.pendingRequests = new Map();
|
|
150
|
+
// Tags set via setTag(), kept client-side so reconnects can restore them on
|
|
151
|
+
// the fresh server-side connection (server tags die with the old socket).
|
|
152
|
+
this.clientTags = new Map();
|
|
133
153
|
// Server-specific properties (SmartServe mode)
|
|
134
154
|
this.smartServeRef = null;
|
|
135
155
|
this.smartServeConnectionWrappers = new Map();
|
|
@@ -143,33 +163,70 @@ export class TypedSocket {
|
|
|
143
163
|
* Connects the client to the server using native WebSocket
|
|
144
164
|
*/
|
|
145
165
|
async connect() {
|
|
166
|
+
if (this.stopped || this.clientOptions?.abortSignal?.aborted) {
|
|
167
|
+
throw this.createAbortError();
|
|
168
|
+
}
|
|
146
169
|
const done = plugins.smartpromise.defer();
|
|
170
|
+
let connectionSettled = false;
|
|
171
|
+
let connectionTimeout;
|
|
172
|
+
let abortConnection;
|
|
173
|
+
const settleConnection = (errorArg) => {
|
|
174
|
+
if (connectionSettled) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
connectionSettled = true;
|
|
178
|
+
if (connectionTimeout) {
|
|
179
|
+
clearTimeout(connectionTimeout);
|
|
180
|
+
}
|
|
181
|
+
if (abortConnection) {
|
|
182
|
+
this.stopListeners.delete(abortConnection);
|
|
183
|
+
this.clientOptions?.abortSignal?.removeEventListener('abort', abortConnection);
|
|
184
|
+
}
|
|
185
|
+
if (errorArg) {
|
|
186
|
+
done.reject(errorArg);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
done.resolve();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
147
192
|
this.updateStatus('connecting');
|
|
148
193
|
// Convert HTTP URL to WebSocket URL
|
|
149
194
|
const wsUrl = this.toWebSocketUrl(this.serverUrl);
|
|
150
195
|
console.log(`TypedSocket connecting to ${wsUrl}...`);
|
|
151
196
|
this.websocket = new WebSocket(wsUrl);
|
|
152
|
-
|
|
197
|
+
abortConnection = () => {
|
|
198
|
+
this.websocket?.close();
|
|
199
|
+
settleConnection(this.createAbortError());
|
|
200
|
+
};
|
|
201
|
+
this.stopListeners.add(abortConnection);
|
|
202
|
+
this.clientOptions?.abortSignal?.addEventListener('abort', abortConnection, { once: true });
|
|
203
|
+
connectionTimeout = setTimeout(() => {
|
|
153
204
|
if (this.connectionStatus !== 'connected') {
|
|
154
205
|
console.warn('TypedSocket connection timeout');
|
|
155
206
|
this.websocket?.close();
|
|
156
|
-
|
|
207
|
+
settleConnection(new Error('Connection timeout'));
|
|
157
208
|
}
|
|
158
209
|
}, 10000);
|
|
159
210
|
this.websocket.onopen = () => {
|
|
160
|
-
clearTimeout(connectionTimeout);
|
|
161
211
|
console.log('TypedSocket connected!');
|
|
162
212
|
this.updateStatus('connected');
|
|
163
213
|
this.retryCount = 0;
|
|
164
214
|
this.currentBackoff = this.clientOptions?.initialBackoffMs ?? 1000;
|
|
165
|
-
|
|
215
|
+
settleConnection();
|
|
216
|
+
void this.reapplyClientTags();
|
|
166
217
|
};
|
|
167
218
|
this.websocket.onmessage = async (event) => {
|
|
168
219
|
await this.handleMessage(event.data);
|
|
169
220
|
};
|
|
170
221
|
this.websocket.onclose = () => {
|
|
171
|
-
|
|
172
|
-
|
|
222
|
+
if (this.connectionStatus === 'connected') {
|
|
223
|
+
this.handleDisconnect();
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
settleConnection(this.stopped || this.clientOptions?.abortSignal?.aborted
|
|
227
|
+
? this.createAbortError()
|
|
228
|
+
: new Error('TypedSocket connection closed before it opened'));
|
|
229
|
+
}
|
|
173
230
|
};
|
|
174
231
|
this.websocket.onerror = (error) => {
|
|
175
232
|
console.error('TypedSocket WebSocket error:', error);
|
|
@@ -178,9 +235,14 @@ export class TypedSocket {
|
|
|
178
235
|
await done.promise;
|
|
179
236
|
}
|
|
180
237
|
catch (err) {
|
|
181
|
-
|
|
182
|
-
|
|
238
|
+
if (connectionTimeout) {
|
|
239
|
+
clearTimeout(connectionTimeout);
|
|
240
|
+
}
|
|
241
|
+
if (this.shouldReconnect()) {
|
|
183
242
|
await this.scheduleReconnect();
|
|
243
|
+
if (this.connectionStatus !== 'connected') {
|
|
244
|
+
throw this.stopped || this.clientOptions?.abortSignal?.aborted ? this.createAbortError() : err;
|
|
245
|
+
}
|
|
184
246
|
}
|
|
185
247
|
else {
|
|
186
248
|
throw err;
|
|
@@ -232,7 +294,7 @@ export class TypedSocket {
|
|
|
232
294
|
pending.reject(new Error('TypedSocket disconnected'));
|
|
233
295
|
}
|
|
234
296
|
this.pendingRequests.clear();
|
|
235
|
-
if (this.
|
|
297
|
+
if (this.shouldReconnect()) {
|
|
236
298
|
this.scheduleReconnect();
|
|
237
299
|
}
|
|
238
300
|
}
|
|
@@ -240,17 +302,20 @@ export class TypedSocket {
|
|
|
240
302
|
* Schedules a reconnection attempt with exponential backoff
|
|
241
303
|
*/
|
|
242
304
|
async scheduleReconnect() {
|
|
243
|
-
if (!this.
|
|
305
|
+
if (!this.shouldReconnect())
|
|
244
306
|
return;
|
|
307
|
+
const clientOptions = this.clientOptions;
|
|
245
308
|
this.updateStatus('reconnecting');
|
|
246
309
|
this.retryCount++;
|
|
247
310
|
// Exponential backoff with jitter
|
|
248
311
|
const jitter = this.currentBackoff * 0.2 * (Math.random() * 2 - 1);
|
|
249
|
-
const delay = Math.min(this.currentBackoff + jitter,
|
|
250
|
-
console.log(`TypedSocket reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount}/${
|
|
251
|
-
await
|
|
312
|
+
const delay = Math.min(this.currentBackoff + jitter, clientOptions.maxBackoffMs);
|
|
313
|
+
console.log(`TypedSocket reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount}/${clientOptions.maxRetries})`);
|
|
314
|
+
await this.waitForReconnectDelay(delay);
|
|
315
|
+
if (!this.shouldReconnect())
|
|
316
|
+
return;
|
|
252
317
|
// Increase backoff for next time
|
|
253
|
-
this.currentBackoff = Math.min(this.currentBackoff * 2,
|
|
318
|
+
this.currentBackoff = Math.min(this.currentBackoff * 2, clientOptions.maxBackoffMs);
|
|
254
319
|
try {
|
|
255
320
|
await this.connect();
|
|
256
321
|
}
|
|
@@ -258,6 +323,43 @@ export class TypedSocket {
|
|
|
258
323
|
console.error('TypedSocket reconnection failed:', err);
|
|
259
324
|
}
|
|
260
325
|
}
|
|
326
|
+
shouldReconnect() {
|
|
327
|
+
return Boolean(this.clientOptions?.autoReconnect &&
|
|
328
|
+
!this.stopped &&
|
|
329
|
+
!this.clientOptions.abortSignal?.aborted &&
|
|
330
|
+
this.retryCount < this.clientOptions.maxRetries);
|
|
331
|
+
}
|
|
332
|
+
async waitForReconnectDelay(delayMsArg) {
|
|
333
|
+
await new Promise((resolve) => {
|
|
334
|
+
let timeout;
|
|
335
|
+
let finished = false;
|
|
336
|
+
const finish = () => {
|
|
337
|
+
if (finished)
|
|
338
|
+
return;
|
|
339
|
+
finished = true;
|
|
340
|
+
if (timeout) {
|
|
341
|
+
clearTimeout(timeout);
|
|
342
|
+
}
|
|
343
|
+
this.stopListeners.delete(finish);
|
|
344
|
+
this.clientOptions?.abortSignal?.removeEventListener('abort', finish);
|
|
345
|
+
resolve();
|
|
346
|
+
};
|
|
347
|
+
if (this.clientOptions?.abortSignal?.aborted) {
|
|
348
|
+
finish();
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
if (this.stopped) {
|
|
352
|
+
finish();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
timeout = setTimeout(finish, delayMsArg);
|
|
356
|
+
this.stopListeners.add(finish);
|
|
357
|
+
this.clientOptions?.abortSignal?.addEventListener('abort', finish, { once: true });
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
createAbortError() {
|
|
361
|
+
return new Error('TypedSocket client startup aborted');
|
|
362
|
+
}
|
|
261
363
|
/**
|
|
262
364
|
* Updates connection status and notifies subscribers
|
|
263
365
|
*/
|
|
@@ -307,8 +409,9 @@ export class TypedSocket {
|
|
|
307
409
|
*/
|
|
308
410
|
createTypedRequest(methodName, targetConnection) {
|
|
309
411
|
const postMethod = async (requestDataArg) => {
|
|
412
|
+
const typedRequestData = requestDataArg;
|
|
310
413
|
if (this.side === 'client') {
|
|
311
|
-
return this.sendRequest(
|
|
414
|
+
return this.sendRequest(typedRequestData);
|
|
312
415
|
}
|
|
313
416
|
// Server-side: send to target connection
|
|
314
417
|
if (!this.smartServeRef) {
|
|
@@ -328,10 +431,14 @@ export class TypedSocket {
|
|
|
328
431
|
throw new Error('Multiple connections available - specify targetConnection');
|
|
329
432
|
}
|
|
330
433
|
}
|
|
434
|
+
typedRequestData.correlation ||= {
|
|
435
|
+
id: plugins.smartstring.create.createCryptoRandomString(),
|
|
436
|
+
phase: 'request',
|
|
437
|
+
};
|
|
331
438
|
// Register interest for response
|
|
332
|
-
const interest = await this.typedrouter.fireEventInterestMap.addInterest(
|
|
439
|
+
const interest = await this.typedrouter.fireEventInterestMap.addInterest(typedRequestData.correlation.id, typedRequestData);
|
|
333
440
|
// Send request
|
|
334
|
-
target.peer.send(plugins.smartjson.stringify(
|
|
441
|
+
target.peer.send(plugins.smartjson.stringify(typedRequestData));
|
|
335
442
|
// Wait for response
|
|
336
443
|
return await interest.interestFullfilled;
|
|
337
444
|
};
|
|
@@ -348,13 +455,25 @@ export class TypedSocket {
|
|
|
348
455
|
*/
|
|
349
456
|
async stop() {
|
|
350
457
|
if (this.side === 'client') {
|
|
458
|
+
this.stopped = true;
|
|
459
|
+
for (const stopListener of Array.from(this.stopListeners)) {
|
|
460
|
+
stopListener();
|
|
461
|
+
}
|
|
462
|
+
this.stopListeners.clear();
|
|
351
463
|
if (this.clientOptions) {
|
|
352
464
|
this.clientOptions.autoReconnect = false;
|
|
465
|
+
if (this.clientOptions.abortSignal && this.abortSignalListener) {
|
|
466
|
+
this.clientOptions.abortSignal.removeEventListener('abort', this.abortSignalListener);
|
|
467
|
+
}
|
|
353
468
|
}
|
|
469
|
+
this.abortSignalListener = undefined;
|
|
354
470
|
if (this.websocket) {
|
|
355
471
|
this.websocket.close();
|
|
356
472
|
this.websocket = null;
|
|
357
473
|
}
|
|
474
|
+
for (const pendingRequest of this.pendingRequests.values()) {
|
|
475
|
+
pendingRequest.reject(new Error('TypedSocket stopped'));
|
|
476
|
+
}
|
|
358
477
|
this.pendingRequests.clear();
|
|
359
478
|
}
|
|
360
479
|
else {
|
|
@@ -379,6 +498,25 @@ export class TypedSocket {
|
|
|
379
498
|
if (!response.success) {
|
|
380
499
|
throw new Error('Failed to set tag on server');
|
|
381
500
|
}
|
|
501
|
+
this.clientTags.set(name, payload);
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* Re-applies all client tags after a reconnect, since server-side tags are
|
|
505
|
+
* bound to the previous connection.
|
|
506
|
+
*/
|
|
507
|
+
async reapplyClientTags() {
|
|
508
|
+
for (const [name, payload] of this.clientTags) {
|
|
509
|
+
try {
|
|
510
|
+
const request = this.createTypedRequest('__typedsocket_setTag');
|
|
511
|
+
const response = await request.fire({ name, payload });
|
|
512
|
+
if (!response.success) {
|
|
513
|
+
console.warn(`TypedSocket failed to restore tag ${name} after reconnect`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
catch (error) {
|
|
517
|
+
console.warn(`TypedSocket failed to restore tag ${name} after reconnect: ${error.message}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
382
520
|
}
|
|
383
521
|
/**
|
|
384
522
|
* Removes a tag from this client connection.
|
|
@@ -393,6 +531,7 @@ export class TypedSocket {
|
|
|
393
531
|
if (!response.success) {
|
|
394
532
|
throw new Error('Failed to remove tag on server');
|
|
395
533
|
}
|
|
534
|
+
this.clientTags.delete(name);
|
|
396
535
|
}
|
|
397
536
|
// ============================================================================
|
|
398
537
|
// SERVER-ONLY METHODS
|
|
@@ -465,4 +604,4 @@ export class TypedSocket {
|
|
|
465
604
|
return allResults[0];
|
|
466
605
|
}
|
|
467
606
|
}
|
|
468
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZWRzb2NrZXQuY2xhc3Nlcy50eXBlZHNvY2tldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3R5cGVkc29ja2V0LmNsYXNzZXMudHlwZWRzb2NrZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUtwRCxNQUFNLFVBQVUsR0FBRyxxQkFBcUIsQ0FBQztBQW1DekM7O0dBRUc7QUFDSCxTQUFTLGtCQUFrQixDQUFDLElBQTRCO0lBQ3RELE9BQU87UUFDTCxJQUFJO1FBQ0osS0FBSyxDQUFDLFVBQVUsQ0FBQyxLQUFhO1lBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQixPQUFPLFNBQVMsQ0FBQztZQUNuQixDQUFDO1lBQ0QsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNoQyxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxNQUFNLE9BQU8sV0FBVztJQUN0QiwrRUFBK0U7SUFDL0UsaUJBQWlCO0lBQ2pCLCtFQUErRTtJQUUvRTs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FpQkc7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FDOUIsY0FBZ0QsRUFDaEQsWUFBb0IsRUFDcEIsVUFBcUMsRUFBRTtRQUV2QyxNQUFNLGNBQWMsR0FBd0M7WUFDMUQsYUFBYSxFQUFFLElBQUk7WUFDbkIsVUFBVSxFQUFFLEdBQUc7WUFDZixnQkFBZ0IsRUFBRSxJQUFJO1lBQ3RCLFlBQVksRUFBRSxLQUFLO1NBQ3BCLENBQUM7UUFDRixNQUFNLElBQUksR0FBRyxFQUFFLEdBQUcsY0FBYyxFQUFFLEdBQUcs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
|
|
607
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZWRzb2NrZXQuY2xhc3Nlcy50eXBlZHNvY2tldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3R5cGVkc29ja2V0LmNsYXNzZXMudHlwZWRzb2NrZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUtwRCxNQUFNLFVBQVUsR0FBRyxxQkFBcUIsQ0FBQztBQW9DekM7O0dBRUc7QUFDSCxTQUFTLGtCQUFrQixDQUFDLElBQTRCO0lBQ3RELE9BQU87UUFDTCxJQUFJO1FBQ0osS0FBSyxDQUFDLFVBQVUsQ0FBQyxLQUFhO1lBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQixPQUFPLFNBQVMsQ0FBQztZQUNuQixDQUFDO1lBQ0QsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNoQyxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxNQUFNLE9BQU8sV0FBVztJQUN0QiwrRUFBK0U7SUFDL0UsaUJBQWlCO0lBQ2pCLCtFQUErRTtJQUUvRTs7Ozs7Ozs7Ozs7Ozs7Ozs7T0FpQkc7SUFDSSxNQUFNLENBQUMsS0FBSyxDQUFDLFlBQVksQ0FDOUIsY0FBZ0QsRUFDaEQsWUFBb0IsRUFDcEIsVUFBcUMsRUFBRTtRQUV2QyxNQUFNLGNBQWMsR0FBNkQ7WUFDL0UsYUFBYSxFQUFFLElBQUk7WUFDbkIsVUFBVSxFQUFFLEdBQUc7WUFDZixnQkFBZ0IsRUFBRSxJQUFJO1lBQ3RCLFlBQVksRUFBRSxLQUFLO1NBQ3BCLENBQUM7UUFDRixNQUFNLElBQUksR0FBRyxFQUFFLEdBQUcsY0FBYyxFQUFFLEdBQUcs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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@api.global/typedsocket",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A library for creating typed WebSocket connections, supporting bi-directional communication with type safety.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -8,15 +8,19 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"author": "Lossless GmbH",
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "(tstest test/ --verbose)",
|
|
13
|
+
"build": "(tsbuild --web --allowimplicitany --skiplibcheck)",
|
|
14
|
+
"buildDocs": "tsdoc"
|
|
15
|
+
},
|
|
11
16
|
"devDependencies": {
|
|
12
|
-
"@git.zone/tsbuild": "^
|
|
13
|
-
"@git.zone/tsbundle": "^2.
|
|
14
|
-
"@git.zone/tsrun": "^2.0.
|
|
15
|
-
"@git.zone/tstest": "^3.
|
|
17
|
+
"@git.zone/tsbuild": "^4.4.2",
|
|
18
|
+
"@git.zone/tsbundle": "^2.10.4",
|
|
19
|
+
"@git.zone/tsrun": "^2.0.4",
|
|
20
|
+
"@git.zone/tstest": "^3.6.6",
|
|
16
21
|
"@push.rocks/smartenv": "^6.0.0",
|
|
17
22
|
"@push.rocks/smartserve": "^1.1.2",
|
|
18
|
-
"@
|
|
19
|
-
"@types/node": "^24.10.1"
|
|
23
|
+
"@types/node": "25.9.1"
|
|
20
24
|
},
|
|
21
25
|
"dependencies": {
|
|
22
26
|
"@api.global/typedrequest": "^3.1.11",
|
|
@@ -54,9 +58,5 @@
|
|
|
54
58
|
"TypeScript",
|
|
55
59
|
"Networking"
|
|
56
60
|
],
|
|
57
|
-
"
|
|
58
|
-
|
|
59
|
-
"build": "(tsbuild --web --allowimplicitany --skiplibcheck)",
|
|
60
|
-
"buildDocs": "tsdoc"
|
|
61
|
-
}
|
|
62
|
-
}
|
|
61
|
+
"packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34"
|
|
62
|
+
}
|
package/readme.md
CHANGED
|
@@ -58,11 +58,12 @@ interface IGreetingRequest extends typedrequestInterfaces.implementsTR<
|
|
|
58
58
|
|
|
59
59
|
### Server Setup
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
TypedSocket binds to a SmartServe WebSocket server. Create the SmartServe instance with a typed router, then expose it through `TypedSocket.fromSmartServe()`:
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
64
|
import { TypedSocket } from '@api.global/typedsocket';
|
|
65
65
|
import * as typedrequest from '@api.global/typedrequest';
|
|
66
|
+
import { SmartServe } from '@push.rocks/smartserve';
|
|
66
67
|
|
|
67
68
|
// Create the router and add handlers
|
|
68
69
|
const typedRouter = new typedrequest.TypedRouter();
|
|
@@ -75,8 +76,15 @@ typedRouter.addTypedHandler<IGreetingRequest>(
|
|
|
75
76
|
})
|
|
76
77
|
);
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
const smartServe = new SmartServe({
|
|
80
|
+
port: 3000,
|
|
81
|
+
websocket: {
|
|
82
|
+
typedRouter,
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
await smartServe.start();
|
|
86
|
+
|
|
87
|
+
const server = TypedSocket.fromSmartServe(smartServe, typedRouter);
|
|
80
88
|
```
|
|
81
89
|
|
|
82
90
|
#### Integration with SmartServe
|
|
@@ -142,6 +150,33 @@ const client = await TypedSocket.createClient(
|
|
|
142
150
|
);
|
|
143
151
|
```
|
|
144
152
|
|
|
153
|
+
#### Abortable Startup
|
|
154
|
+
|
|
155
|
+
Pass an `AbortSignal` when startup or reconnect attempts must be cancellable. Aborting stops the in-flight WebSocket and prevents queued reconnect attempts from continuing.
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
const abortController = new AbortController();
|
|
159
|
+
|
|
160
|
+
const clientPromise = TypedSocket.createClient(
|
|
161
|
+
clientRouter,
|
|
162
|
+
'http://localhost:3000',
|
|
163
|
+
{
|
|
164
|
+
abortSignal: abortController.signal,
|
|
165
|
+
initialBackoffMs: 1000,
|
|
166
|
+
maxRetries: 10,
|
|
167
|
+
}
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
// Later, if the connection attempt should no longer continue:
|
|
171
|
+
abortController.abort();
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const client = await clientPromise;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
// Startup was aborted before a stable connection was established.
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
145
180
|
#### Using Window Location (Browser)
|
|
146
181
|
|
|
147
182
|
In browser environments, you can automatically use the current page's origin:
|
|
@@ -196,7 +231,7 @@ interface IUserTag extends typedrequestInterfaces.ITag {
|
|
|
196
231
|
payload: 'admin' | 'user' | 'guest';
|
|
197
232
|
}
|
|
198
233
|
|
|
199
|
-
client.
|
|
234
|
+
await client.setTag<IUserTag>('userRole', 'admin');
|
|
200
235
|
```
|
|
201
236
|
|
|
202
237
|
```typescript
|
|
@@ -221,11 +256,11 @@ const firstAdmin = await server.findTargetConnectionByTag<IUserTag>('userRole',
|
|
|
221
256
|
Subscribe to connection status events:
|
|
222
257
|
|
|
223
258
|
```typescript
|
|
224
|
-
client.
|
|
259
|
+
client.statusSubject.subscribe((status) => {
|
|
225
260
|
console.log('Connection status:', status);
|
|
226
261
|
});
|
|
227
262
|
|
|
228
|
-
server.
|
|
263
|
+
server.statusSubject.subscribe((status) => {
|
|
229
264
|
console.log('Server connection event:', status);
|
|
230
265
|
});
|
|
231
266
|
```
|
|
@@ -238,8 +273,8 @@ Properly close connections when done:
|
|
|
238
273
|
// Client
|
|
239
274
|
await client.stop();
|
|
240
275
|
|
|
241
|
-
//
|
|
242
|
-
await
|
|
276
|
+
// SmartServe owns the server-side WebSocket lifecycle
|
|
277
|
+
await smartServe.stop();
|
|
243
278
|
```
|
|
244
279
|
|
|
245
280
|
## API Reference
|
|
@@ -250,8 +285,7 @@ await server.stop();
|
|
|
250
285
|
|
|
251
286
|
| Method | Description |
|
|
252
287
|
|--------|-------------|
|
|
253
|
-
| `
|
|
254
|
-
| `createClient(router, serverUrl, alias?)` | Creates a WebSocket client that connects to the specified server URL. |
|
|
288
|
+
| `createClient(router, serverUrl, options?)` | Creates a WebSocket client that connects to the specified server URL. Options include `autoReconnect`, `maxRetries`, `initialBackoffMs`, `maxBackoffMs`, and `abortSignal`. |
|
|
255
289
|
| `fromSmartServe(smartServe, router)` | Creates a TypedSocket bound to an existing SmartServe instance. |
|
|
256
290
|
| `useWindowLocationOriginUrl()` | Returns the current window location origin (browser only). |
|
|
257
291
|
|
|
@@ -261,14 +295,15 @@ await server.stop();
|
|
|
261
295
|
|----------|-------------|
|
|
262
296
|
| `side` | Whether this instance is a `'server'` or `'client'`. |
|
|
263
297
|
| `typedrouter` | The TypedRouter instance handling requests. |
|
|
264
|
-
| `
|
|
298
|
+
| `statusSubject` | RxJS Subject for connection status events. |
|
|
265
299
|
|
|
266
300
|
#### Instance Methods
|
|
267
301
|
|
|
268
302
|
| Method | Description |
|
|
269
303
|
|--------|-------------|
|
|
270
304
|
| `createTypedRequest(method, targetConnection?)` | Creates a typed request for the specified method. |
|
|
271
|
-
| `
|
|
305
|
+
| `setTag(name, payload)` | Sets a tag on the client connection (client-side only). |
|
|
306
|
+
| `removeTag(name)` | Removes a tag from the client connection (client-side only). |
|
|
272
307
|
| `findAllTargetConnections(filterFn)` | Finds all connections matching the filter (server-side only). |
|
|
273
308
|
| `findTargetConnection(filterFn)` | Finds the first connection matching the filter (server-side only). |
|
|
274
309
|
| `findAllTargetConnectionsByTag(key, payload?)` | Finds all connections with the specified tag. |
|
|
@@ -277,7 +312,7 @@ await server.stop();
|
|
|
277
312
|
|
|
278
313
|
## License and Legal Information
|
|
279
314
|
|
|
280
|
-
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [
|
|
315
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](./license) file.
|
|
281
316
|
|
|
282
317
|
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
283
318
|
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -28,6 +28,7 @@ export interface ITypedSocketClientOptions {
|
|
|
28
28
|
maxRetries?: number;
|
|
29
29
|
initialBackoffMs?: number;
|
|
30
30
|
maxBackoffMs?: number;
|
|
31
|
+
abortSignal?: AbortSignal;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
@@ -82,7 +83,7 @@ export class TypedSocket {
|
|
|
82
83
|
serverUrlArg: string,
|
|
83
84
|
options: ITypedSocketClientOptions = {}
|
|
84
85
|
): Promise<TypedSocket> {
|
|
85
|
-
const defaultOptions: Required<ITypedSocketClientOptions> = {
|
|
86
|
+
const defaultOptions: Omit<Required<ITypedSocketClientOptions>, 'abortSignal'> = {
|
|
86
87
|
autoReconnect: true,
|
|
87
88
|
maxRetries: 100,
|
|
88
89
|
initialBackoffMs: 1000,
|
|
@@ -95,7 +96,22 @@ export class TypedSocket {
|
|
|
95
96
|
typedSocket.serverUrl = serverUrlArg;
|
|
96
97
|
typedSocket.currentBackoff = opts.initialBackoffMs;
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
if (opts.abortSignal?.aborted) {
|
|
100
|
+
throw typedSocket.createAbortError();
|
|
101
|
+
}
|
|
102
|
+
if (opts.abortSignal) {
|
|
103
|
+
typedSocket.abortSignalListener = () => {
|
|
104
|
+
void typedSocket.stop();
|
|
105
|
+
};
|
|
106
|
+
opts.abortSignal.addEventListener('abort', typedSocket.abortSignalListener, { once: true });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await typedSocket.connect();
|
|
111
|
+
} catch (error) {
|
|
112
|
+
await typedSocket.stop();
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
99
115
|
|
|
100
116
|
return typedSocket;
|
|
101
117
|
}
|
|
@@ -195,14 +211,20 @@ export class TypedSocket {
|
|
|
195
211
|
|
|
196
212
|
// Client-specific properties
|
|
197
213
|
private websocket: WebSocket | null = null;
|
|
198
|
-
private clientOptions: Required<ITypedSocketClientOptions> | null = null;
|
|
214
|
+
private clientOptions: (Omit<Required<ITypedSocketClientOptions>, 'abortSignal'> & Pick<ITypedSocketClientOptions, 'abortSignal'>) | null = null;
|
|
199
215
|
private serverUrl: string = '';
|
|
200
216
|
private retryCount = 0;
|
|
201
217
|
private currentBackoff = 1000;
|
|
218
|
+
private stopped = false;
|
|
219
|
+
private abortSignalListener?: () => void;
|
|
220
|
+
private stopListeners = new Set<() => void>();
|
|
202
221
|
private pendingRequests = new Map<string, {
|
|
203
222
|
resolve: (response: any) => void;
|
|
204
223
|
reject: (error: Error) => void;
|
|
205
224
|
}>();
|
|
225
|
+
// Tags set via setTag(), kept client-side so reconnects can restore them on
|
|
226
|
+
// the fresh server-side connection (server tags die with the old socket).
|
|
227
|
+
private clientTags = new Map<string, unknown>();
|
|
206
228
|
|
|
207
229
|
// Server-specific properties (SmartServe mode)
|
|
208
230
|
private smartServeRef: plugins.SmartServe | null = null;
|
|
@@ -228,7 +250,33 @@ export class TypedSocket {
|
|
|
228
250
|
* Connects the client to the server using native WebSocket
|
|
229
251
|
*/
|
|
230
252
|
private async connect(): Promise<void> {
|
|
253
|
+
if (this.stopped || this.clientOptions?.abortSignal?.aborted) {
|
|
254
|
+
throw this.createAbortError();
|
|
255
|
+
}
|
|
256
|
+
|
|
231
257
|
const done = plugins.smartpromise.defer<void>();
|
|
258
|
+
let connectionSettled = false;
|
|
259
|
+
let connectionTimeout: ReturnType<typeof setTimeout> | undefined;
|
|
260
|
+
let abortConnection: (() => void) | undefined;
|
|
261
|
+
|
|
262
|
+
const settleConnection = (errorArg?: Error) => {
|
|
263
|
+
if (connectionSettled) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
connectionSettled = true;
|
|
267
|
+
if (connectionTimeout) {
|
|
268
|
+
clearTimeout(connectionTimeout);
|
|
269
|
+
}
|
|
270
|
+
if (abortConnection) {
|
|
271
|
+
this.stopListeners.delete(abortConnection);
|
|
272
|
+
this.clientOptions?.abortSignal?.removeEventListener('abort', abortConnection);
|
|
273
|
+
}
|
|
274
|
+
if (errorArg) {
|
|
275
|
+
done.reject(errorArg);
|
|
276
|
+
} else {
|
|
277
|
+
done.resolve();
|
|
278
|
+
}
|
|
279
|
+
};
|
|
232
280
|
|
|
233
281
|
this.updateStatus('connecting');
|
|
234
282
|
|
|
@@ -238,21 +286,28 @@ export class TypedSocket {
|
|
|
238
286
|
|
|
239
287
|
this.websocket = new WebSocket(wsUrl);
|
|
240
288
|
|
|
241
|
-
|
|
289
|
+
abortConnection = () => {
|
|
290
|
+
this.websocket?.close();
|
|
291
|
+
settleConnection(this.createAbortError());
|
|
292
|
+
};
|
|
293
|
+
this.stopListeners.add(abortConnection);
|
|
294
|
+
this.clientOptions?.abortSignal?.addEventListener('abort', abortConnection, { once: true });
|
|
295
|
+
|
|
296
|
+
connectionTimeout = setTimeout(() => {
|
|
242
297
|
if (this.connectionStatus !== 'connected') {
|
|
243
298
|
console.warn('TypedSocket connection timeout');
|
|
244
299
|
this.websocket?.close();
|
|
245
|
-
|
|
300
|
+
settleConnection(new Error('Connection timeout'));
|
|
246
301
|
}
|
|
247
302
|
}, 10000);
|
|
248
303
|
|
|
249
304
|
this.websocket.onopen = () => {
|
|
250
|
-
clearTimeout(connectionTimeout);
|
|
251
305
|
console.log('TypedSocket connected!');
|
|
252
306
|
this.updateStatus('connected');
|
|
253
307
|
this.retryCount = 0;
|
|
254
308
|
this.currentBackoff = this.clientOptions?.initialBackoffMs ?? 1000;
|
|
255
|
-
|
|
309
|
+
settleConnection();
|
|
310
|
+
void this.reapplyClientTags();
|
|
256
311
|
};
|
|
257
312
|
|
|
258
313
|
this.websocket.onmessage = async (event) => {
|
|
@@ -260,8 +315,13 @@ export class TypedSocket {
|
|
|
260
315
|
};
|
|
261
316
|
|
|
262
317
|
this.websocket.onclose = () => {
|
|
263
|
-
|
|
264
|
-
|
|
318
|
+
if (this.connectionStatus === 'connected') {
|
|
319
|
+
this.handleDisconnect();
|
|
320
|
+
} else {
|
|
321
|
+
settleConnection(this.stopped || this.clientOptions?.abortSignal?.aborted
|
|
322
|
+
? this.createAbortError()
|
|
323
|
+
: new Error('TypedSocket connection closed before it opened'));
|
|
324
|
+
}
|
|
265
325
|
};
|
|
266
326
|
|
|
267
327
|
this.websocket.onerror = (error) => {
|
|
@@ -271,9 +331,14 @@ export class TypedSocket {
|
|
|
271
331
|
try {
|
|
272
332
|
await done.promise;
|
|
273
333
|
} catch (err) {
|
|
274
|
-
|
|
275
|
-
|
|
334
|
+
if (connectionTimeout) {
|
|
335
|
+
clearTimeout(connectionTimeout);
|
|
336
|
+
}
|
|
337
|
+
if (this.shouldReconnect()) {
|
|
276
338
|
await this.scheduleReconnect();
|
|
339
|
+
if (this.connectionStatus !== 'connected') {
|
|
340
|
+
throw this.stopped || this.clientOptions?.abortSignal?.aborted ? this.createAbortError() : err;
|
|
341
|
+
}
|
|
277
342
|
} else {
|
|
278
343
|
throw err;
|
|
279
344
|
}
|
|
@@ -331,7 +396,7 @@ export class TypedSocket {
|
|
|
331
396
|
}
|
|
332
397
|
this.pendingRequests.clear();
|
|
333
398
|
|
|
334
|
-
if (this.
|
|
399
|
+
if (this.shouldReconnect()) {
|
|
335
400
|
this.scheduleReconnect();
|
|
336
401
|
}
|
|
337
402
|
}
|
|
@@ -340,21 +405,24 @@ export class TypedSocket {
|
|
|
340
405
|
* Schedules a reconnection attempt with exponential backoff
|
|
341
406
|
*/
|
|
342
407
|
private async scheduleReconnect(): Promise<void> {
|
|
343
|
-
if (!this.
|
|
408
|
+
if (!this.shouldReconnect()) return;
|
|
409
|
+
const clientOptions = this.clientOptions!;
|
|
344
410
|
|
|
345
411
|
this.updateStatus('reconnecting');
|
|
346
412
|
this.retryCount++;
|
|
347
413
|
|
|
348
414
|
// Exponential backoff with jitter
|
|
349
415
|
const jitter = this.currentBackoff * 0.2 * (Math.random() * 2 - 1);
|
|
350
|
-
const delay = Math.min(this.currentBackoff + jitter,
|
|
416
|
+
const delay = Math.min(this.currentBackoff + jitter, clientOptions.maxBackoffMs);
|
|
351
417
|
|
|
352
|
-
console.log(`TypedSocket reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount}/${
|
|
418
|
+
console.log(`TypedSocket reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount}/${clientOptions.maxRetries})`);
|
|
353
419
|
|
|
354
|
-
await
|
|
420
|
+
await this.waitForReconnectDelay(delay);
|
|
421
|
+
|
|
422
|
+
if (!this.shouldReconnect()) return;
|
|
355
423
|
|
|
356
424
|
// Increase backoff for next time
|
|
357
|
-
this.currentBackoff = Math.min(this.currentBackoff * 2,
|
|
425
|
+
this.currentBackoff = Math.min(this.currentBackoff * 2, clientOptions.maxBackoffMs);
|
|
358
426
|
|
|
359
427
|
try {
|
|
360
428
|
await this.connect();
|
|
@@ -363,6 +431,47 @@ export class TypedSocket {
|
|
|
363
431
|
}
|
|
364
432
|
}
|
|
365
433
|
|
|
434
|
+
private shouldReconnect(): boolean {
|
|
435
|
+
return Boolean(
|
|
436
|
+
this.clientOptions?.autoReconnect &&
|
|
437
|
+
!this.stopped &&
|
|
438
|
+
!this.clientOptions.abortSignal?.aborted &&
|
|
439
|
+
this.retryCount < this.clientOptions.maxRetries,
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
private async waitForReconnectDelay(delayMsArg: number): Promise<void> {
|
|
444
|
+
await new Promise<void>((resolve) => {
|
|
445
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
446
|
+
let finished = false;
|
|
447
|
+
const finish = () => {
|
|
448
|
+
if (finished) return;
|
|
449
|
+
finished = true;
|
|
450
|
+
if (timeout) {
|
|
451
|
+
clearTimeout(timeout);
|
|
452
|
+
}
|
|
453
|
+
this.stopListeners.delete(finish);
|
|
454
|
+
this.clientOptions?.abortSignal?.removeEventListener('abort', finish);
|
|
455
|
+
resolve();
|
|
456
|
+
};
|
|
457
|
+
if (this.clientOptions?.abortSignal?.aborted) {
|
|
458
|
+
finish();
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
if (this.stopped) {
|
|
462
|
+
finish();
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
timeout = setTimeout(finish, delayMsArg);
|
|
466
|
+
this.stopListeners.add(finish);
|
|
467
|
+
this.clientOptions?.abortSignal?.addEventListener('abort', finish, { once: true });
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private createAbortError(): Error {
|
|
472
|
+
return new Error('TypedSocket client startup aborted');
|
|
473
|
+
}
|
|
474
|
+
|
|
366
475
|
/**
|
|
367
476
|
* Updates connection status and notifies subscribers
|
|
368
477
|
*/
|
|
@@ -423,9 +532,12 @@ export class TypedSocket {
|
|
|
423
532
|
methodName: T['method'],
|
|
424
533
|
targetConnection?: ISmartServeConnectionWrapper
|
|
425
534
|
): plugins.typedrequest.TypedRequest<T> {
|
|
426
|
-
const postMethod = async (
|
|
535
|
+
const postMethod = async (
|
|
536
|
+
requestDataArg: plugins.typedrequestInterfaces.ITypedRequest,
|
|
537
|
+
): Promise<plugins.typedrequestInterfaces.ITypedRequest> => {
|
|
538
|
+
const typedRequestData = requestDataArg as T;
|
|
427
539
|
if (this.side === 'client') {
|
|
428
|
-
return this.sendRequest(
|
|
540
|
+
return this.sendRequest(typedRequestData);
|
|
429
541
|
}
|
|
430
542
|
|
|
431
543
|
// Server-side: send to target connection
|
|
@@ -446,14 +558,19 @@ export class TypedSocket {
|
|
|
446
558
|
}
|
|
447
559
|
}
|
|
448
560
|
|
|
561
|
+
typedRequestData.correlation ||= {
|
|
562
|
+
id: plugins.smartstring.create.createCryptoRandomString(),
|
|
563
|
+
phase: 'request',
|
|
564
|
+
};
|
|
565
|
+
|
|
449
566
|
// Register interest for response
|
|
450
567
|
const interest = await this.typedrouter.fireEventInterestMap.addInterest(
|
|
451
|
-
|
|
452
|
-
|
|
568
|
+
typedRequestData.correlation.id,
|
|
569
|
+
typedRequestData,
|
|
453
570
|
);
|
|
454
571
|
|
|
455
572
|
// Send request
|
|
456
|
-
target.peer.send(plugins.smartjson.stringify(
|
|
573
|
+
target.peer.send(plugins.smartjson.stringify(typedRequestData));
|
|
457
574
|
|
|
458
575
|
// Wait for response
|
|
459
576
|
return await interest.interestFullfilled as T;
|
|
@@ -477,13 +594,25 @@ export class TypedSocket {
|
|
|
477
594
|
*/
|
|
478
595
|
public async stop(): Promise<void> {
|
|
479
596
|
if (this.side === 'client') {
|
|
597
|
+
this.stopped = true;
|
|
598
|
+
for (const stopListener of Array.from(this.stopListeners)) {
|
|
599
|
+
stopListener();
|
|
600
|
+
}
|
|
601
|
+
this.stopListeners.clear();
|
|
480
602
|
if (this.clientOptions) {
|
|
481
603
|
this.clientOptions.autoReconnect = false;
|
|
604
|
+
if (this.clientOptions.abortSignal && this.abortSignalListener) {
|
|
605
|
+
this.clientOptions.abortSignal.removeEventListener('abort', this.abortSignalListener);
|
|
606
|
+
}
|
|
482
607
|
}
|
|
608
|
+
this.abortSignalListener = undefined;
|
|
483
609
|
if (this.websocket) {
|
|
484
610
|
this.websocket.close();
|
|
485
611
|
this.websocket = null;
|
|
486
612
|
}
|
|
613
|
+
for (const pendingRequest of this.pendingRequests.values()) {
|
|
614
|
+
pendingRequest.reject(new Error('TypedSocket stopped'));
|
|
615
|
+
}
|
|
487
616
|
this.pendingRequests.clear();
|
|
488
617
|
} else {
|
|
489
618
|
// Server mode - just clear wrappers (SmartServe manages its own lifecycle)
|
|
@@ -514,6 +643,25 @@ export class TypedSocket {
|
|
|
514
643
|
if (!response.success) {
|
|
515
644
|
throw new Error('Failed to set tag on server');
|
|
516
645
|
}
|
|
646
|
+
this.clientTags.set(name, payload);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* Re-applies all client tags after a reconnect, since server-side tags are
|
|
651
|
+
* bound to the previous connection.
|
|
652
|
+
*/
|
|
653
|
+
private async reapplyClientTags(): Promise<void> {
|
|
654
|
+
for (const [name, payload] of this.clientTags) {
|
|
655
|
+
try {
|
|
656
|
+
const request = this.createTypedRequest<IReq_SetClientTag>('__typedsocket_setTag');
|
|
657
|
+
const response = await request.fire({ name, payload });
|
|
658
|
+
if (!response.success) {
|
|
659
|
+
console.warn(`TypedSocket failed to restore tag ${name} after reconnect`);
|
|
660
|
+
}
|
|
661
|
+
} catch (error) {
|
|
662
|
+
console.warn(`TypedSocket failed to restore tag ${name} after reconnect: ${(error as Error).message}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
517
665
|
}
|
|
518
666
|
|
|
519
667
|
/**
|
|
@@ -531,6 +679,7 @@ export class TypedSocket {
|
|
|
531
679
|
if (!response.success) {
|
|
532
680
|
throw new Error('Failed to remove tag on server');
|
|
533
681
|
}
|
|
682
|
+
this.clientTags.delete(name);
|
|
534
683
|
}
|
|
535
684
|
|
|
536
685
|
// ============================================================================
|