@api.global/typedsocket 5.1.2 → 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.clienttagreconciler.d.ts +51 -0
- package/dist_ts/classes.clienttagreconciler.js +290 -0
- package/dist_ts/classes.nativebyteerror.d.ts +13 -0
- package/dist_ts/classes.nativebyteerror.js +26 -0
- package/dist_ts/classes.nativebytemanager.d.ts +327 -0
- package/dist_ts/classes.nativebytemanager.js +2343 -0
- package/dist_ts/{typedsocket.classes.typedsocket.d.ts → classes.typedsocket.d.ts} +122 -13
- package/dist_ts/classes.typedsocket.js +1733 -0
- package/dist_ts/classes.typedsockettagpolicymanager.d.ts +112 -0
- package/dist_ts/classes.typedsockettagpolicymanager.js +549 -0
- package/dist_ts/constants.nativebytes.d.ts +59 -0
- package/dist_ts/constants.nativebytes.js +58 -0
- package/dist_ts/helpers.nativebytecodec.d.ts +8 -0
- package/dist_ts/helpers.nativebytecodec.js +328 -0
- package/dist_ts/helpers.websocket.d.ts +8 -0
- package/dist_ts/helpers.websocket.js +13 -0
- package/dist_ts/index.d.ts +9 -1
- package/dist_ts/index.js +6 -2
- package/dist_ts/interfaces.diagnostics.d.ts +73 -0
- package/dist_ts/interfaces.diagnostics.js +2 -0
- package/dist_ts/interfaces.nativebytes.d.ts +187 -0
- package/dist_ts/interfaces.nativebytes.js +2 -0
- package/{ts/typedsocket.plugins.ts → dist_ts/plugins.d.ts} +3 -9
- package/dist_ts/plugins.js +14 -0
- package/{license → license.md} +2 -2
- package/package.json +22 -18
- package/readme.hints.md +60 -19
- package/readme.md +536 -56
- package/dist_ts/typedsocket.classes.typedsocket.js +0 -941
- package/dist_ts/typedsocket.plugins.d.ts +0 -11
- package/dist_ts/typedsocket.plugins.js +0 -13
- package/npmextra.json +0 -35
- package/ts/00_commitinfo_data.ts +0 -8
- package/ts/index.ts +0 -1
- package/ts/typedsocket.classes.typedsocket.ts +0 -1156
|
@@ -1,941 +0,0 @@
|
|
|
1
|
-
import * as plugins from './typedsocket.plugins.js';
|
|
2
|
-
const TAG_PREFIX = '__typedsocket_tag__';
|
|
3
|
-
/**
|
|
4
|
-
* Creates a wrapper around IWebSocketPeer for tag compatibility
|
|
5
|
-
*/
|
|
6
|
-
function wrapSmartServePeer(peer) {
|
|
7
|
-
return {
|
|
8
|
-
peer,
|
|
9
|
-
async getTagById(tagId) {
|
|
10
|
-
if (!peer.tags.has(tagId)) {
|
|
11
|
-
return undefined;
|
|
12
|
-
}
|
|
13
|
-
const payload = peer.data.get(`${TAG_PREFIX}${tagId}`);
|
|
14
|
-
return { id: tagId, payload };
|
|
15
|
-
},
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
function composeAbortSignals(configuredSignalArg, requestSignalArg) {
|
|
19
|
-
if (!configuredSignalArg || configuredSignalArg === requestSignalArg) {
|
|
20
|
-
return {
|
|
21
|
-
signal: requestSignalArg ?? configuredSignalArg,
|
|
22
|
-
cleanup: () => { },
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
if (!requestSignalArg) {
|
|
26
|
-
return {
|
|
27
|
-
signal: configuredSignalArg,
|
|
28
|
-
cleanup: () => { },
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const abortController = new AbortController();
|
|
32
|
-
const configuredAbortListener = () => {
|
|
33
|
-
abortController.abort(configuredSignalArg.reason);
|
|
34
|
-
};
|
|
35
|
-
const requestAbortListener = () => {
|
|
36
|
-
abortController.abort(requestSignalArg.reason);
|
|
37
|
-
};
|
|
38
|
-
const cleanup = () => {
|
|
39
|
-
configuredSignalArg.removeEventListener('abort', configuredAbortListener);
|
|
40
|
-
requestSignalArg.removeEventListener('abort', requestAbortListener);
|
|
41
|
-
};
|
|
42
|
-
if (configuredSignalArg.aborted) {
|
|
43
|
-
configuredAbortListener();
|
|
44
|
-
}
|
|
45
|
-
else if (requestSignalArg.aborted) {
|
|
46
|
-
requestAbortListener();
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
configuredSignalArg.addEventListener('abort', configuredAbortListener, { once: true });
|
|
50
|
-
requestSignalArg.addEventListener('abort', requestAbortListener, { once: true });
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
signal: abortController.signal,
|
|
54
|
-
cleanup,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
export class TypedSocket {
|
|
58
|
-
// ============================================================================
|
|
59
|
-
// STATIC METHODS
|
|
60
|
-
// ============================================================================
|
|
61
|
-
/**
|
|
62
|
-
* Creates a TypedSocket client using native WebSocket.
|
|
63
|
-
* Works in both browser and Node.js environments.
|
|
64
|
-
*
|
|
65
|
-
* @param typedrouterArg - TypedRouter for handling server-initiated requests
|
|
66
|
-
* @param serverUrlArg - Server URL (e.g., 'http://localhost:3000' or 'wss://example.com')
|
|
67
|
-
* @param options - Connection options
|
|
68
|
-
*
|
|
69
|
-
* @example
|
|
70
|
-
* ```typescript
|
|
71
|
-
* const typedRouter = new TypedRouter();
|
|
72
|
-
* const client = await TypedSocket.createClient(
|
|
73
|
-
* typedRouter,
|
|
74
|
-
* 'http://localhost:3000',
|
|
75
|
-
* { autoReconnect: true }
|
|
76
|
-
* );
|
|
77
|
-
* ```
|
|
78
|
-
*/
|
|
79
|
-
static async createClient(typedrouterArg, serverUrlArg, options = {}) {
|
|
80
|
-
const defaultOptions = {
|
|
81
|
-
autoReconnect: true,
|
|
82
|
-
maxRetries: 100,
|
|
83
|
-
initialBackoffMs: 1000,
|
|
84
|
-
maxBackoffMs: 60000,
|
|
85
|
-
};
|
|
86
|
-
const opts = { ...defaultOptions, ...options };
|
|
87
|
-
const typedSocket = new TypedSocket('client', typedrouterArg);
|
|
88
|
-
typedSocket.clientOptions = opts;
|
|
89
|
-
typedSocket.serverUrl = serverUrlArg;
|
|
90
|
-
typedSocket.currentBackoff = opts.initialBackoffMs;
|
|
91
|
-
if (opts.abortSignal?.aborted) {
|
|
92
|
-
throw typedSocket.createAbortError();
|
|
93
|
-
}
|
|
94
|
-
if (opts.abortSignal) {
|
|
95
|
-
typedSocket.abortSignalListener = () => {
|
|
96
|
-
void typedSocket.stop();
|
|
97
|
-
};
|
|
98
|
-
opts.abortSignal.addEventListener('abort', typedSocket.abortSignalListener, { once: true });
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
await typedSocket.connect();
|
|
102
|
-
}
|
|
103
|
-
catch (error) {
|
|
104
|
-
await typedSocket.stop();
|
|
105
|
-
throw error;
|
|
106
|
-
}
|
|
107
|
-
return typedSocket;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Returns the current window location origin URL.
|
|
111
|
-
* Useful in browser environments for connecting to the same origin.
|
|
112
|
-
*/
|
|
113
|
-
static { this.useWindowLocationOriginUrl = () => {
|
|
114
|
-
return plugins.smarturl.Smarturl.createFromUrl(globalThis.location.origin).toString();
|
|
115
|
-
}; }
|
|
116
|
-
/**
|
|
117
|
-
* Creates a server-side TypedSocket and composes its private protocol
|
|
118
|
-
* handlers into one or more isolated application routers.
|
|
119
|
-
*
|
|
120
|
-
* Call attachSmartServe() after constructing the SmartServe transport.
|
|
121
|
-
*/
|
|
122
|
-
static createServer(typedRouterOrRoutersArg) {
|
|
123
|
-
const typedRouters = Array.isArray(typedRouterOrRoutersArg)
|
|
124
|
-
? [...typedRouterOrRoutersArg]
|
|
125
|
-
: [typedRouterOrRoutersArg];
|
|
126
|
-
if (typedRouters.length === 0) {
|
|
127
|
-
throw new Error('TypedSocket.createServer requires at least one TypedRouter.');
|
|
128
|
-
}
|
|
129
|
-
if (new Set(typedRouters).size !== typedRouters.length) {
|
|
130
|
-
throw new Error('TypedSocket.createServer received the same TypedRouter more than once.');
|
|
131
|
-
}
|
|
132
|
-
const protocolRouter = new plugins.typedrequest.TypedRouter();
|
|
133
|
-
TypedSocket.registerTagHandlers(protocolRouter);
|
|
134
|
-
const protocolMethods = new Set(protocolRouter.getMethodNames());
|
|
135
|
-
for (const typedRouter of typedRouters) {
|
|
136
|
-
const collision = typedRouter
|
|
137
|
-
.getMethodNames()
|
|
138
|
-
.find((methodArg) => protocolMethods.has(methodArg));
|
|
139
|
-
if (collision) {
|
|
140
|
-
throw new Error(`TypedSocket protocol handler collides with application method "${collision}".`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const detachProtocolRouters = [];
|
|
144
|
-
try {
|
|
145
|
-
for (const typedRouter of typedRouters) {
|
|
146
|
-
detachProtocolRouters.push(typedRouter.addFallbackRouter(protocolRouter));
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
catch (error) {
|
|
150
|
-
for (const detachProtocolRouter of detachProtocolRouters.reverse()) {
|
|
151
|
-
detachProtocolRouter();
|
|
152
|
-
}
|
|
153
|
-
throw error;
|
|
154
|
-
}
|
|
155
|
-
const typedSocket = new TypedSocket('server', typedRouters[0]);
|
|
156
|
-
typedSocket.serverTypedRouters = typedRouters;
|
|
157
|
-
typedSocket.detachProtocolRouters = detachProtocolRouters;
|
|
158
|
-
return typedSocket;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Creates and attaches a TypedSocket server to an existing SmartServe
|
|
162
|
-
* instance. Prefer createServer() plus attachSmartServe() when transport
|
|
163
|
-
* construction needs the composed routers first.
|
|
164
|
-
*
|
|
165
|
-
* @param smartServeArg - SmartServe instance configured with the application router
|
|
166
|
-
* @param typedRouterArg - Application TypedRouter used by SmartServe
|
|
167
|
-
*
|
|
168
|
-
* @example
|
|
169
|
-
* ```typescript
|
|
170
|
-
* const typedRouter = new TypedRouter();
|
|
171
|
-
* const smartServe = new SmartServe({
|
|
172
|
-
* port: 3000,
|
|
173
|
-
* websocket: {
|
|
174
|
-
* typedRouter,
|
|
175
|
-
* onConnectionOpen: (peer) => peer.tags.add('client')
|
|
176
|
-
* }
|
|
177
|
-
* });
|
|
178
|
-
* await smartServe.start();
|
|
179
|
-
* const typedSocket = TypedSocket.fromSmartServe(smartServe, typedRouter);
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
static fromSmartServe(smartServeArg, typedRouterArg) {
|
|
183
|
-
return TypedSocket
|
|
184
|
-
.createServer(typedRouterArg)
|
|
185
|
-
.attachSmartServe(smartServeArg);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Registers built-in TypedHandlers for tag management
|
|
189
|
-
*/
|
|
190
|
-
static registerTagHandlers(typedRouter) {
|
|
191
|
-
// Set tag handler
|
|
192
|
-
typedRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('__typedsocket_setTag', async (data, meta) => {
|
|
193
|
-
const peer = meta?.localData?.peer;
|
|
194
|
-
if (!peer) {
|
|
195
|
-
console.warn('setTag: No peer found in request context');
|
|
196
|
-
return { success: false };
|
|
197
|
-
}
|
|
198
|
-
peer.tags.add(data.name);
|
|
199
|
-
peer.data.set(`${TAG_PREFIX}${data.name}`, data.payload);
|
|
200
|
-
return { success: true };
|
|
201
|
-
}));
|
|
202
|
-
// Remove tag handler
|
|
203
|
-
typedRouter.addTypedHandler(new plugins.typedrequest.TypedHandler('__typedsocket_removeTag', async (data, meta) => {
|
|
204
|
-
const peer = meta?.localData?.peer;
|
|
205
|
-
if (!peer) {
|
|
206
|
-
console.warn('removeTag: No peer found in request context');
|
|
207
|
-
return { success: false };
|
|
208
|
-
}
|
|
209
|
-
peer.tags.delete(data.name);
|
|
210
|
-
peer.data.delete(`${TAG_PREFIX}${data.name}`);
|
|
211
|
-
return { success: true };
|
|
212
|
-
}));
|
|
213
|
-
}
|
|
214
|
-
// ============================================================================
|
|
215
|
-
// CONSTRUCTOR
|
|
216
|
-
// ============================================================================
|
|
217
|
-
constructor(sideArg, typedrouterArg) {
|
|
218
|
-
// Connection status observable
|
|
219
|
-
this.statusSubject = new plugins.smartrx.rxjs.Subject();
|
|
220
|
-
this.connectionStatus = 'new';
|
|
221
|
-
// Client-specific properties
|
|
222
|
-
this.websocket = null;
|
|
223
|
-
this.clientOptions = null;
|
|
224
|
-
this.serverUrl = '';
|
|
225
|
-
this.retryCount = 0;
|
|
226
|
-
this.currentBackoff = 1000;
|
|
227
|
-
this.stopped = false;
|
|
228
|
-
this.stopListeners = new Set();
|
|
229
|
-
this.pendingRequests = new Map();
|
|
230
|
-
// Tags set via setTag(), kept client-side so reconnects can restore them on
|
|
231
|
-
// the fresh server-side connection (server tags die with the old socket).
|
|
232
|
-
this.clientTags = new Map();
|
|
233
|
-
this.clientTagMutations = new Map();
|
|
234
|
-
// Server-specific properties (SmartServe mode)
|
|
235
|
-
this.smartServeRef = null;
|
|
236
|
-
this.serverTypedRouters = [];
|
|
237
|
-
this.detachProtocolRouters = [];
|
|
238
|
-
this.serverStopping = false;
|
|
239
|
-
this.unsubscribeSmartServeConnectionClose = null;
|
|
240
|
-
this.pendingServerRequests = new Map();
|
|
241
|
-
this.pendingServerRequestIdsByPeerId = new Map();
|
|
242
|
-
this.side = sideArg;
|
|
243
|
-
this.typedrouter = typedrouterArg;
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Attaches the SmartServe transport after server router composition.
|
|
247
|
-
*/
|
|
248
|
-
attachSmartServe(smartServeArg) {
|
|
249
|
-
if (this.side !== 'server') {
|
|
250
|
-
throw new Error('attachSmartServe is only available on servers.');
|
|
251
|
-
}
|
|
252
|
-
if (this.serverStopping) {
|
|
253
|
-
throw new Error('Cannot attach SmartServe after the TypedSocket server has stopped.');
|
|
254
|
-
}
|
|
255
|
-
if (this.smartServeRef) {
|
|
256
|
-
if (this.smartServeRef === smartServeArg) {
|
|
257
|
-
return this;
|
|
258
|
-
}
|
|
259
|
-
throw new Error('TypedSocket server is already attached to a SmartServe instance.');
|
|
260
|
-
}
|
|
261
|
-
const unsubscribe = smartServeArg.subscribeWebSocketConnectionClose((peerArg) => {
|
|
262
|
-
this.cancelPendingServerRequestsForPeer(peerArg.id, new Error(`TypedSocket target connection closed: ${peerArg.id}`));
|
|
263
|
-
});
|
|
264
|
-
this.smartServeRef = smartServeArg;
|
|
265
|
-
this.unsubscribeSmartServeConnectionClose = unsubscribe;
|
|
266
|
-
return this;
|
|
267
|
-
}
|
|
268
|
-
// ============================================================================
|
|
269
|
-
// CLIENT METHODS
|
|
270
|
-
// ============================================================================
|
|
271
|
-
/**
|
|
272
|
-
* Connects the client to the server using native WebSocket
|
|
273
|
-
*/
|
|
274
|
-
async connect() {
|
|
275
|
-
if (this.stopped || this.clientOptions?.abortSignal?.aborted) {
|
|
276
|
-
throw this.createAbortError();
|
|
277
|
-
}
|
|
278
|
-
const done = plugins.smartpromise.defer();
|
|
279
|
-
let connectionSettled = false;
|
|
280
|
-
let connectionTimeout;
|
|
281
|
-
let abortConnection;
|
|
282
|
-
const settleConnection = (errorArg) => {
|
|
283
|
-
if (connectionSettled) {
|
|
284
|
-
return;
|
|
285
|
-
}
|
|
286
|
-
connectionSettled = true;
|
|
287
|
-
if (connectionTimeout) {
|
|
288
|
-
clearTimeout(connectionTimeout);
|
|
289
|
-
}
|
|
290
|
-
if (abortConnection) {
|
|
291
|
-
this.stopListeners.delete(abortConnection);
|
|
292
|
-
this.clientOptions?.abortSignal?.removeEventListener('abort', abortConnection);
|
|
293
|
-
}
|
|
294
|
-
if (errorArg) {
|
|
295
|
-
done.reject(errorArg);
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
done.resolve();
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
this.updateStatus('connecting');
|
|
302
|
-
// Convert HTTP URL to WebSocket URL
|
|
303
|
-
const wsUrl = this.toWebSocketUrl(this.serverUrl);
|
|
304
|
-
this.logDebug(`TypedSocket connecting to ${wsUrl}...`);
|
|
305
|
-
this.websocket = new WebSocket(wsUrl);
|
|
306
|
-
abortConnection = () => {
|
|
307
|
-
this.websocket?.close();
|
|
308
|
-
settleConnection(this.createAbortError());
|
|
309
|
-
};
|
|
310
|
-
this.stopListeners.add(abortConnection);
|
|
311
|
-
this.clientOptions?.abortSignal?.addEventListener('abort', abortConnection, { once: true });
|
|
312
|
-
connectionTimeout = setTimeout(() => {
|
|
313
|
-
if (this.connectionStatus !== 'connected') {
|
|
314
|
-
this.logTransient(`TypedSocket connection timeout for ${wsUrl}`);
|
|
315
|
-
this.websocket?.close();
|
|
316
|
-
settleConnection(new Error('Connection timeout'));
|
|
317
|
-
}
|
|
318
|
-
}, 10000);
|
|
319
|
-
this.websocket.onopen = async () => {
|
|
320
|
-
const openedWebsocket = this.websocket;
|
|
321
|
-
try {
|
|
322
|
-
await this.reapplyClientTags();
|
|
323
|
-
if (this.websocket !== openedWebsocket || openedWebsocket?.readyState !== WebSocket.OPEN) {
|
|
324
|
-
throw new Error('TypedSocket connection changed while restoring client tags');
|
|
325
|
-
}
|
|
326
|
-
this.logDebug('TypedSocket connected and client registration restored!');
|
|
327
|
-
this.retryCount = 0;
|
|
328
|
-
this.currentBackoff = this.clientOptions?.initialBackoffMs ?? 1000;
|
|
329
|
-
this.updateStatus('connected');
|
|
330
|
-
settleConnection();
|
|
331
|
-
}
|
|
332
|
-
catch (error) {
|
|
333
|
-
const restorationError = error instanceof Error
|
|
334
|
-
? error
|
|
335
|
-
: new Error(String(error));
|
|
336
|
-
this.logTransient(`TypedSocket client registration restoration failed: ${restorationError.message}`);
|
|
337
|
-
settleConnection(restorationError);
|
|
338
|
-
openedWebsocket?.close();
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
this.websocket.onmessage = async (event) => {
|
|
342
|
-
await this.handleMessage(event.data);
|
|
343
|
-
};
|
|
344
|
-
this.websocket.onclose = () => {
|
|
345
|
-
if (this.connectionStatus === 'connected') {
|
|
346
|
-
this.handleDisconnect();
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
this.rejectPendingClientRequests(new Error('TypedSocket disconnected'));
|
|
350
|
-
settleConnection(this.stopped || this.clientOptions?.abortSignal?.aborted
|
|
351
|
-
? this.createAbortError()
|
|
352
|
-
: new Error('TypedSocket connection closed before it opened'));
|
|
353
|
-
}
|
|
354
|
-
};
|
|
355
|
-
this.websocket.onerror = () => {
|
|
356
|
-
// transient socket errors (network blips, proxy TLS hiccups) are expected
|
|
357
|
-
// and handled by reconnection; only exhausted retries surface as errors
|
|
358
|
-
this.logTransient(`TypedSocket websocket error on ${wsUrl}`);
|
|
359
|
-
};
|
|
360
|
-
try {
|
|
361
|
-
await done.promise;
|
|
362
|
-
}
|
|
363
|
-
catch (err) {
|
|
364
|
-
if (connectionTimeout) {
|
|
365
|
-
clearTimeout(connectionTimeout);
|
|
366
|
-
}
|
|
367
|
-
if (this.shouldReconnect()) {
|
|
368
|
-
await this.scheduleReconnect();
|
|
369
|
-
if (this.connectionStatus !== 'connected') {
|
|
370
|
-
throw this.stopped || this.clientOptions?.abortSignal?.aborted ? this.createAbortError() : err;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
else {
|
|
374
|
-
throw err;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
|
-
* Converts an HTTP(S) URL to a WebSocket URL
|
|
380
|
-
*/
|
|
381
|
-
toWebSocketUrl(url) {
|
|
382
|
-
const parsed = new URL(url);
|
|
383
|
-
const wsProtocol = (() => {
|
|
384
|
-
switch (parsed.protocol) {
|
|
385
|
-
case 'http:':
|
|
386
|
-
case 'ws:':
|
|
387
|
-
return 'ws:';
|
|
388
|
-
case 'https:':
|
|
389
|
-
case 'wss:':
|
|
390
|
-
return 'wss:';
|
|
391
|
-
default:
|
|
392
|
-
throw new Error('TypedSocket server URL must use http, https, ws, or wss.');
|
|
393
|
-
}
|
|
394
|
-
})();
|
|
395
|
-
return `${wsProtocol}//${parsed.host}${parsed.pathname}`;
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Handles incoming WebSocket messages
|
|
399
|
-
*/
|
|
400
|
-
async handleMessage(data) {
|
|
401
|
-
try {
|
|
402
|
-
const messageText = typeof data === 'string' ? data : new TextDecoder().decode(data);
|
|
403
|
-
const message = plugins.smartjson.parse(messageText);
|
|
404
|
-
// Check if this is a response to a pending request
|
|
405
|
-
if (message.correlation?.id && this.pendingRequests.has(message.correlation.id)) {
|
|
406
|
-
const pending = this.pendingRequests.get(message.correlation.id);
|
|
407
|
-
this.pendingRequests.delete(message.correlation.id);
|
|
408
|
-
pending.resolve(message);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
// Server-initiated request - route through TypedRouter
|
|
412
|
-
const response = await this.typedrouter.routeAndAddResponse(message);
|
|
413
|
-
if (response && this.websocket?.readyState === WebSocket.OPEN) {
|
|
414
|
-
this.websocket.send(plugins.smartjson.stringify(response));
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
catch (err) {
|
|
418
|
-
console.error('TypedSocket failed to process message:', err);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Handles WebSocket disconnection
|
|
423
|
-
*/
|
|
424
|
-
handleDisconnect() {
|
|
425
|
-
if (this.connectionStatus === 'disconnected') {
|
|
426
|
-
return; // Already handled
|
|
427
|
-
}
|
|
428
|
-
this.updateStatus('disconnected');
|
|
429
|
-
// Reject all pending requests — the connection is gone and they'll never receive a response
|
|
430
|
-
this.rejectPendingClientRequests(new Error('TypedSocket disconnected'));
|
|
431
|
-
if (this.shouldReconnect()) {
|
|
432
|
-
this.scheduleReconnect();
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* Schedules a reconnection attempt with exponential backoff
|
|
437
|
-
*/
|
|
438
|
-
async scheduleReconnect() {
|
|
439
|
-
if (!this.shouldReconnect())
|
|
440
|
-
return;
|
|
441
|
-
const clientOptions = this.clientOptions;
|
|
442
|
-
this.updateStatus('reconnecting');
|
|
443
|
-
this.retryCount++;
|
|
444
|
-
// Exponential backoff with jitter
|
|
445
|
-
const jitter = this.currentBackoff * 0.2 * (Math.random() * 2 - 1);
|
|
446
|
-
const delay = Math.min(this.currentBackoff + jitter, clientOptions.maxBackoffMs);
|
|
447
|
-
this.logDebug(`TypedSocket reconnecting in ${Math.round(delay)}ms (attempt ${this.retryCount}/${clientOptions.maxRetries})`);
|
|
448
|
-
await this.waitForReconnectDelay(delay);
|
|
449
|
-
if (!this.shouldReconnect())
|
|
450
|
-
return;
|
|
451
|
-
// Increase backoff for next time
|
|
452
|
-
this.currentBackoff = Math.min(this.currentBackoff * 2, clientOptions.maxBackoffMs);
|
|
453
|
-
try {
|
|
454
|
-
await this.connect();
|
|
455
|
-
}
|
|
456
|
-
catch (err) {
|
|
457
|
-
if (this.shouldReconnect()) {
|
|
458
|
-
this.logDebug(`TypedSocket reconnection attempt failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
459
|
-
}
|
|
460
|
-
else if (!this.stopped && !this.clientOptions?.abortSignal?.aborted) {
|
|
461
|
-
console.error(`TypedSocket giving up on ${this.serverUrl} after ${this.retryCount} attempts:`, err instanceof Error ? err.message : err);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
/**
|
|
466
|
-
* Lifecycle chatter: hidden by default in browser devtools (verbose level)
|
|
467
|
-
* and irrelevant for servers, but available when debugging.
|
|
468
|
-
*/
|
|
469
|
-
logDebug(messageArg) {
|
|
470
|
-
console.debug(messageArg);
|
|
471
|
-
}
|
|
472
|
-
/**
|
|
473
|
-
* Failures that reconnection is expected to recover from. Logged as debug
|
|
474
|
-
* while retries remain; the final failure is reported by scheduleReconnect.
|
|
475
|
-
*/
|
|
476
|
-
logTransient(messageArg) {
|
|
477
|
-
if (this.shouldReconnect()) {
|
|
478
|
-
this.logDebug(messageArg);
|
|
479
|
-
}
|
|
480
|
-
else if (!this.stopped && !this.clientOptions?.abortSignal?.aborted) {
|
|
481
|
-
console.warn(messageArg);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
shouldReconnect() {
|
|
485
|
-
return Boolean(this.clientOptions?.autoReconnect &&
|
|
486
|
-
!this.stopped &&
|
|
487
|
-
!this.clientOptions.abortSignal?.aborted &&
|
|
488
|
-
this.retryCount < this.clientOptions.maxRetries);
|
|
489
|
-
}
|
|
490
|
-
async waitForReconnectDelay(delayMsArg) {
|
|
491
|
-
await new Promise((resolve) => {
|
|
492
|
-
let timeout;
|
|
493
|
-
let finished = false;
|
|
494
|
-
const finish = () => {
|
|
495
|
-
if (finished)
|
|
496
|
-
return;
|
|
497
|
-
finished = true;
|
|
498
|
-
if (timeout) {
|
|
499
|
-
clearTimeout(timeout);
|
|
500
|
-
}
|
|
501
|
-
this.stopListeners.delete(finish);
|
|
502
|
-
this.clientOptions?.abortSignal?.removeEventListener('abort', finish);
|
|
503
|
-
resolve();
|
|
504
|
-
};
|
|
505
|
-
if (this.clientOptions?.abortSignal?.aborted) {
|
|
506
|
-
finish();
|
|
507
|
-
return;
|
|
508
|
-
}
|
|
509
|
-
if (this.stopped) {
|
|
510
|
-
finish();
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
timeout = setTimeout(finish, delayMsArg);
|
|
514
|
-
this.stopListeners.add(finish);
|
|
515
|
-
this.clientOptions?.abortSignal?.addEventListener('abort', finish, { once: true });
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
createAbortError() {
|
|
519
|
-
return new Error('TypedSocket client startup aborted');
|
|
520
|
-
}
|
|
521
|
-
rejectPendingClientRequests(errorArg) {
|
|
522
|
-
for (const pending of this.pendingRequests.values()) {
|
|
523
|
-
pending.reject(errorArg);
|
|
524
|
-
}
|
|
525
|
-
this.pendingRequests.clear();
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* Updates connection status and notifies subscribers
|
|
529
|
-
*/
|
|
530
|
-
updateStatus(status) {
|
|
531
|
-
if (this.connectionStatus !== status) {
|
|
532
|
-
this.connectionStatus = status;
|
|
533
|
-
this.statusSubject.next(status);
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Sends a request to the server and waits for response (client-side)
|
|
538
|
-
*/
|
|
539
|
-
async sendRequest(request, optionsArg = {}) {
|
|
540
|
-
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
|
|
541
|
-
throw new Error('WebSocket not connected');
|
|
542
|
-
}
|
|
543
|
-
const timeoutMs = this.normalizeRequestTimeout(optionsArg.timeoutMs);
|
|
544
|
-
if (optionsArg.abortSignal?.aborted) {
|
|
545
|
-
throw new Error('TypedSocket request aborted');
|
|
546
|
-
}
|
|
547
|
-
request.correlation ||= {
|
|
548
|
-
id: plugins.smartstring.create.createCryptoRandomString(),
|
|
549
|
-
phase: 'request',
|
|
550
|
-
};
|
|
551
|
-
const correlationId = request.correlation.id;
|
|
552
|
-
return new Promise((resolve, reject) => {
|
|
553
|
-
let settled = false;
|
|
554
|
-
const cleanup = () => {
|
|
555
|
-
clearTimeout(timeout);
|
|
556
|
-
optionsArg.abortSignal?.removeEventListener('abort', onAbort);
|
|
557
|
-
this.pendingRequests.delete(correlationId);
|
|
558
|
-
};
|
|
559
|
-
const settle = (errorArg, responseArg) => {
|
|
560
|
-
if (settled) {
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
settled = true;
|
|
564
|
-
cleanup();
|
|
565
|
-
if (errorArg) {
|
|
566
|
-
reject(errorArg);
|
|
567
|
-
}
|
|
568
|
-
else {
|
|
569
|
-
resolve(responseArg);
|
|
570
|
-
}
|
|
571
|
-
};
|
|
572
|
-
const onAbort = () => settle(new Error('TypedSocket request aborted'));
|
|
573
|
-
const timeout = setTimeout(() => {
|
|
574
|
-
settle(new Error('TypedSocket request timed out'));
|
|
575
|
-
}, timeoutMs);
|
|
576
|
-
this.pendingRequests.set(correlationId, {
|
|
577
|
-
resolve: (response) => {
|
|
578
|
-
settle(undefined, response);
|
|
579
|
-
},
|
|
580
|
-
reject: (error) => {
|
|
581
|
-
settle(error);
|
|
582
|
-
},
|
|
583
|
-
});
|
|
584
|
-
optionsArg.abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
585
|
-
if (optionsArg.abortSignal?.aborted) {
|
|
586
|
-
onAbort();
|
|
587
|
-
return;
|
|
588
|
-
}
|
|
589
|
-
try {
|
|
590
|
-
this.websocket.send(plugins.smartjson.stringify(request));
|
|
591
|
-
}
|
|
592
|
-
catch (error) {
|
|
593
|
-
settle(error instanceof Error ? error : new Error(String(error)));
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
normalizeRequestTimeout(timeoutMsArg) {
|
|
598
|
-
const timeoutMs = timeoutMsArg ?? 30000;
|
|
599
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs < 0) {
|
|
600
|
-
throw new Error('TypedSocket request timeout must be a finite non-negative number');
|
|
601
|
-
}
|
|
602
|
-
return timeoutMs;
|
|
603
|
-
}
|
|
604
|
-
cancelPendingServerRequestsForPeer(peerIdArg, errorArg) {
|
|
605
|
-
const correlationIds = this.pendingServerRequestIdsByPeerId.get(peerIdArg);
|
|
606
|
-
if (!correlationIds) {
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
for (const correlationId of Array.from(correlationIds)) {
|
|
610
|
-
this.pendingServerRequests.get(correlationId)?.cancel(errorArg);
|
|
611
|
-
}
|
|
612
|
-
}
|
|
613
|
-
async sendServerRequest(typedRequestDataArg, targetArg, optionsArg) {
|
|
614
|
-
const timeoutMs = this.normalizeRequestTimeout(optionsArg.timeoutMs);
|
|
615
|
-
if (this.serverStopping || !this.smartServeRef) {
|
|
616
|
-
throw new Error('TypedSocket server stopped');
|
|
617
|
-
}
|
|
618
|
-
if (optionsArg.abortSignal?.aborted) {
|
|
619
|
-
throw new Error('TypedSocket request aborted');
|
|
620
|
-
}
|
|
621
|
-
typedRequestDataArg.correlation ||= {
|
|
622
|
-
id: plugins.smartstring.create.createCryptoRandomString(),
|
|
623
|
-
phase: 'request',
|
|
624
|
-
};
|
|
625
|
-
const correlationId = typedRequestDataArg.correlation.id;
|
|
626
|
-
const peerId = targetArg.peer.id;
|
|
627
|
-
if (this.pendingServerRequests.has(correlationId)) {
|
|
628
|
-
throw new Error(`Duplicate in-flight TypedSocket correlation ID: ${correlationId}`);
|
|
629
|
-
}
|
|
630
|
-
const cancellation = plugins.smartpromise.defer();
|
|
631
|
-
// A stop/abort may happen while InterestMap.addInterest is yielding. Attach
|
|
632
|
-
// a rejection handler immediately; Promise.race below still receives the
|
|
633
|
-
// original rejection once the interest exists.
|
|
634
|
-
void cancellation.promise.catch(() => { });
|
|
635
|
-
const cleanup = plugins.smartpromise.defer();
|
|
636
|
-
let cancellationRequested = false;
|
|
637
|
-
const cancel = (errorArg) => {
|
|
638
|
-
if (cancellationRequested) {
|
|
639
|
-
return;
|
|
640
|
-
}
|
|
641
|
-
cancellationRequested = true;
|
|
642
|
-
cancellation.reject(errorArg);
|
|
643
|
-
};
|
|
644
|
-
const pendingRequest = {
|
|
645
|
-
peerId,
|
|
646
|
-
cancel,
|
|
647
|
-
cleanupComplete: cleanup.promise,
|
|
648
|
-
};
|
|
649
|
-
this.pendingServerRequests.set(correlationId, pendingRequest);
|
|
650
|
-
const requestsForPeer = this.pendingServerRequestIdsByPeerId.get(peerId) ?? new Set();
|
|
651
|
-
requestsForPeer.add(correlationId);
|
|
652
|
-
this.pendingServerRequestIdsByPeerId.set(peerId, requestsForPeer);
|
|
653
|
-
const onAbort = () => cancel(new Error('TypedSocket request aborted'));
|
|
654
|
-
const timeout = setTimeout(() => cancel(new Error(`TypedSocket request timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
655
|
-
optionsArg.abortSignal?.addEventListener('abort', onAbort, { once: true });
|
|
656
|
-
if (optionsArg.abortSignal?.aborted) {
|
|
657
|
-
onAbort();
|
|
658
|
-
}
|
|
659
|
-
const destroyInterests = [];
|
|
660
|
-
try {
|
|
661
|
-
const interestPromises = [];
|
|
662
|
-
for (const typedRouter of this.serverTypedRouters) {
|
|
663
|
-
const interestRegistration = typedRouter.fireEventInterestMap
|
|
664
|
-
.addInterest(correlationId, typedRequestDataArg)
|
|
665
|
-
.then((interestArg) => {
|
|
666
|
-
if (cancellationRequested || this.serverStopping) {
|
|
667
|
-
interestArg.destroy();
|
|
668
|
-
}
|
|
669
|
-
return interestArg;
|
|
670
|
-
});
|
|
671
|
-
const interest = await Promise.race([
|
|
672
|
-
interestRegistration,
|
|
673
|
-
cancellation.promise,
|
|
674
|
-
]);
|
|
675
|
-
if (cancellationRequested || this.serverStopping) {
|
|
676
|
-
interest.destroy();
|
|
677
|
-
await cancellation.promise;
|
|
678
|
-
}
|
|
679
|
-
destroyInterests.push(() => interest.destroy());
|
|
680
|
-
interestPromises.push(interest.interestFullfilled);
|
|
681
|
-
}
|
|
682
|
-
// Establish the rejecting race before send so synchronous close/abort and
|
|
683
|
-
// send failures all use the same cleanup path.
|
|
684
|
-
const response = Promise.race([
|
|
685
|
-
...interestPromises,
|
|
686
|
-
cancellation.promise,
|
|
687
|
-
]);
|
|
688
|
-
const targetIsStillConnected = this.smartServeRef
|
|
689
|
-
?.getWebSocketConnections()
|
|
690
|
-
.some((peerArg) => peerArg.id === peerId) ?? false;
|
|
691
|
-
if (!targetIsStillConnected) {
|
|
692
|
-
cancel(new Error(`TypedSocket target connection closed: ${peerId}`));
|
|
693
|
-
}
|
|
694
|
-
if (this.serverStopping) {
|
|
695
|
-
cancel(new Error('TypedSocket server stopped'));
|
|
696
|
-
}
|
|
697
|
-
if (!cancellationRequested && targetIsStillConnected && !this.serverStopping) {
|
|
698
|
-
targetArg.peer.send(plugins.smartjson.stringify(typedRequestDataArg));
|
|
699
|
-
}
|
|
700
|
-
return await response;
|
|
701
|
-
}
|
|
702
|
-
finally {
|
|
703
|
-
clearTimeout(timeout);
|
|
704
|
-
optionsArg.abortSignal?.removeEventListener('abort', onAbort);
|
|
705
|
-
this.pendingServerRequests.delete(correlationId);
|
|
706
|
-
const remainingRequestsForPeer = this.pendingServerRequestIdsByPeerId.get(peerId);
|
|
707
|
-
remainingRequestsForPeer?.delete(correlationId);
|
|
708
|
-
if (remainingRequestsForPeer?.size === 0) {
|
|
709
|
-
this.pendingServerRequestIdsByPeerId.delete(peerId);
|
|
710
|
-
}
|
|
711
|
-
for (const destroyInterest of destroyInterests) {
|
|
712
|
-
destroyInterest();
|
|
713
|
-
}
|
|
714
|
-
cleanup.resolve();
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
// ============================================================================
|
|
718
|
-
// PUBLIC API - SHARED
|
|
719
|
-
// ============================================================================
|
|
720
|
-
/**
|
|
721
|
-
* Creates a TypedRequest for the specified method.
|
|
722
|
-
* On clients, sends to the server.
|
|
723
|
-
* On servers, sends to the specified target connection.
|
|
724
|
-
*/
|
|
725
|
-
createTypedRequest(methodName, targetConnection, optionsArg = {}) {
|
|
726
|
-
const postMethod = async (requestDataArg, postOptionsArg = {}) => {
|
|
727
|
-
const typedRequestData = requestDataArg;
|
|
728
|
-
const composedAbortSignal = composeAbortSignals(optionsArg.abortSignal, postOptionsArg.signal);
|
|
729
|
-
const requestOptions = {
|
|
730
|
-
...optionsArg,
|
|
731
|
-
abortSignal: composedAbortSignal.signal,
|
|
732
|
-
};
|
|
733
|
-
try {
|
|
734
|
-
if (this.side === 'client') {
|
|
735
|
-
return await this.sendRequest(typedRequestData, requestOptions);
|
|
736
|
-
}
|
|
737
|
-
// Server-side: send to target connection
|
|
738
|
-
if (!this.smartServeRef) {
|
|
739
|
-
throw new Error('Server not initialized');
|
|
740
|
-
}
|
|
741
|
-
let target = targetConnection;
|
|
742
|
-
if (!target) {
|
|
743
|
-
const allConnections = this.smartServeRef.getWebSocketConnections();
|
|
744
|
-
if (allConnections.length === 1) {
|
|
745
|
-
const peer = allConnections[0];
|
|
746
|
-
target = wrapSmartServePeer(peer);
|
|
747
|
-
}
|
|
748
|
-
else if (allConnections.length === 0) {
|
|
749
|
-
throw new Error('No WebSocket connections available');
|
|
750
|
-
}
|
|
751
|
-
else {
|
|
752
|
-
throw new Error('Multiple connections available - specify targetConnection');
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
return await this.sendServerRequest(typedRequestData, target, requestOptions);
|
|
756
|
-
}
|
|
757
|
-
finally {
|
|
758
|
-
composedAbortSignal.cleanup();
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
return new plugins.typedrequest.TypedRequest(new plugins.typedrequest.TypedTarget({
|
|
762
|
-
postMethod,
|
|
763
|
-
supportsAbortSignal: true,
|
|
764
|
-
}), methodName);
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Gets the current connection status
|
|
768
|
-
*/
|
|
769
|
-
getStatus() {
|
|
770
|
-
return this.connectionStatus;
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* Stops the TypedSocket client or cleans up server state
|
|
774
|
-
*/
|
|
775
|
-
async stop() {
|
|
776
|
-
if (this.side === 'client') {
|
|
777
|
-
this.stopped = true;
|
|
778
|
-
for (const stopListener of Array.from(this.stopListeners)) {
|
|
779
|
-
stopListener();
|
|
780
|
-
}
|
|
781
|
-
this.stopListeners.clear();
|
|
782
|
-
if (this.clientOptions) {
|
|
783
|
-
this.clientOptions.autoReconnect = false;
|
|
784
|
-
if (this.clientOptions.abortSignal && this.abortSignalListener) {
|
|
785
|
-
this.clientOptions.abortSignal.removeEventListener('abort', this.abortSignalListener);
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
this.abortSignalListener = undefined;
|
|
789
|
-
if (this.websocket) {
|
|
790
|
-
this.websocket.close();
|
|
791
|
-
this.websocket = null;
|
|
792
|
-
}
|
|
793
|
-
for (const pendingRequest of this.pendingRequests.values()) {
|
|
794
|
-
pendingRequest.reject(new Error('TypedSocket stopped'));
|
|
795
|
-
}
|
|
796
|
-
this.pendingRequests.clear();
|
|
797
|
-
}
|
|
798
|
-
else {
|
|
799
|
-
this.serverStopping = true;
|
|
800
|
-
const pendingRequests = Array.from(this.pendingServerRequests.values());
|
|
801
|
-
for (const pendingRequest of pendingRequests) {
|
|
802
|
-
pendingRequest.cancel(new Error('TypedSocket server stopped'));
|
|
803
|
-
}
|
|
804
|
-
await Promise.all(pendingRequests.map((pendingRequest) => pendingRequest.cleanupComplete));
|
|
805
|
-
this.unsubscribeSmartServeConnectionClose?.();
|
|
806
|
-
this.unsubscribeSmartServeConnectionClose = null;
|
|
807
|
-
this.smartServeRef = null;
|
|
808
|
-
for (const detachProtocolRouter of this.detachProtocolRouters.reverse()) {
|
|
809
|
-
detachProtocolRouter();
|
|
810
|
-
}
|
|
811
|
-
this.detachProtocolRouters = [];
|
|
812
|
-
}
|
|
813
|
-
}
|
|
814
|
-
// ============================================================================
|
|
815
|
-
// CLIENT-ONLY METHODS
|
|
816
|
-
// ============================================================================
|
|
817
|
-
/**
|
|
818
|
-
* Sets a tag on this client connection.
|
|
819
|
-
* Tags are stored on the server and can be used for filtering.
|
|
820
|
-
* @client-only
|
|
821
|
-
*/
|
|
822
|
-
async setTag(name, payload) {
|
|
823
|
-
if (this.side !== 'client') {
|
|
824
|
-
throw new Error('setTag is only available on clients');
|
|
825
|
-
}
|
|
826
|
-
const mutation = Symbol(name);
|
|
827
|
-
this.clientTagMutations.set(name, mutation);
|
|
828
|
-
try {
|
|
829
|
-
const request = this.createTypedRequest('__typedsocket_setTag');
|
|
830
|
-
const response = await request.fire({ name, payload });
|
|
831
|
-
if (!response.success) {
|
|
832
|
-
throw new Error('Failed to set tag on server');
|
|
833
|
-
}
|
|
834
|
-
if (this.clientTagMutations.get(name) === mutation) {
|
|
835
|
-
this.clientTags.set(name, payload);
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
finally {
|
|
839
|
-
if (this.clientTagMutations.get(name) === mutation) {
|
|
840
|
-
this.clientTagMutations.delete(name);
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
/**
|
|
845
|
-
* Re-applies all client tags after a reconnect, since server-side tags are
|
|
846
|
-
* bound to the previous connection.
|
|
847
|
-
*/
|
|
848
|
-
async reapplyClientTags() {
|
|
849
|
-
for (const [name, payload] of this.clientTags) {
|
|
850
|
-
const request = this.createTypedRequest('__typedsocket_setTag');
|
|
851
|
-
const response = await request.fire({ name, payload });
|
|
852
|
-
if (!response.success) {
|
|
853
|
-
throw new Error(`TypedSocket failed to restore tag ${name} after reconnect`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
/**
|
|
858
|
-
* Removes a tag from this client connection.
|
|
859
|
-
* @client-only
|
|
860
|
-
*/
|
|
861
|
-
async removeTag(name) {
|
|
862
|
-
if (this.side !== 'client') {
|
|
863
|
-
throw new Error('removeTag is only available on clients');
|
|
864
|
-
}
|
|
865
|
-
const mutation = Symbol(name);
|
|
866
|
-
this.clientTagMutations.set(name, mutation);
|
|
867
|
-
this.clientTags.delete(name);
|
|
868
|
-
try {
|
|
869
|
-
const request = this.createTypedRequest('__typedsocket_removeTag');
|
|
870
|
-
const response = await request.fire({ name });
|
|
871
|
-
if (!response.success) {
|
|
872
|
-
throw new Error('Failed to remove tag on server');
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
finally {
|
|
876
|
-
if (this.clientTagMutations.get(name) === mutation) {
|
|
877
|
-
this.clientTagMutations.delete(name);
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
// ============================================================================
|
|
882
|
-
// SERVER-ONLY METHODS
|
|
883
|
-
// ============================================================================
|
|
884
|
-
/**
|
|
885
|
-
* Finds all connections matching the filter function.
|
|
886
|
-
* @server-only
|
|
887
|
-
*/
|
|
888
|
-
async findAllTargetConnections(asyncFindFuncArg) {
|
|
889
|
-
if (this.side !== 'server' || !this.smartServeRef) {
|
|
890
|
-
throw new Error('findAllTargetConnections is only available on servers');
|
|
891
|
-
}
|
|
892
|
-
const matchingConnections = [];
|
|
893
|
-
for (const peer of this.smartServeRef.getWebSocketConnections()) {
|
|
894
|
-
const wrapper = wrapSmartServePeer(peer);
|
|
895
|
-
if (await asyncFindFuncArg(wrapper)) {
|
|
896
|
-
matchingConnections.push(wrapper);
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
return matchingConnections;
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* Finds the first connection matching the filter function.
|
|
903
|
-
* @server-only
|
|
904
|
-
*/
|
|
905
|
-
async findTargetConnection(asyncFindFuncArg) {
|
|
906
|
-
const allMatching = await this.findAllTargetConnections(asyncFindFuncArg);
|
|
907
|
-
return allMatching[0];
|
|
908
|
-
}
|
|
909
|
-
/**
|
|
910
|
-
* Finds all connections with the specified tag.
|
|
911
|
-
* @server-only
|
|
912
|
-
*/
|
|
913
|
-
async findAllTargetConnectionsByTag(keyArg, payloadArg) {
|
|
914
|
-
if (this.side !== 'server' || !this.smartServeRef) {
|
|
915
|
-
throw new Error('findAllTargetConnectionsByTag is only available on servers');
|
|
916
|
-
}
|
|
917
|
-
const peers = this.smartServeRef.getWebSocketConnectionsByTag(keyArg);
|
|
918
|
-
const results = [];
|
|
919
|
-
for (const peer of peers) {
|
|
920
|
-
const wrapper = wrapSmartServePeer(peer);
|
|
921
|
-
// If payload specified, also filter by payload
|
|
922
|
-
if (payloadArg !== undefined) {
|
|
923
|
-
const tag = await wrapper.getTagById(keyArg);
|
|
924
|
-
if (plugins.smartjson.stringify(tag?.payload) !== plugins.smartjson.stringify(payloadArg)) {
|
|
925
|
-
continue;
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
results.push(wrapper);
|
|
929
|
-
}
|
|
930
|
-
return results;
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Finds the first connection with the specified tag.
|
|
934
|
-
* @server-only
|
|
935
|
-
*/
|
|
936
|
-
async findTargetConnectionByTag(keyArg, payloadArg) {
|
|
937
|
-
const allResults = await this.findAllTargetConnectionsByTag(keyArg, payloadArg);
|
|
938
|
-
return allResults[0];
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZWRzb2NrZXQuY2xhc3Nlcy50eXBlZHNvY2tldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3R5cGVkc29ja2V0LmNsYXNzZXMudHlwZWRzb2NrZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSwwQkFBMEIsQ0FBQztBQUtwRCxNQUFNLFVBQVUsR0FBRyxxQkFBcUIsQ0FBQztBQTJEekM7O0dBRUc7QUFDSCxTQUFTLGtCQUFrQixDQUFDLElBQTRCO0lBQ3RELE9BQU87UUFDTCxJQUFJO1FBQ0osS0FBSyxDQUFDLFVBQVUsQ0FBQyxLQUFhO1lBQzVCLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO2dCQUMxQixPQUFPLFNBQVMsQ0FBQztZQUNuQixDQUFDO1lBQ0QsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxVQUFVLEdBQUcsS0FBSyxFQUFFLENBQUMsQ0FBQztZQUN2RCxPQUFPLEVBQUUsRUFBRSxFQUFFLEtBQUssRUFBRSxPQUFPLEVBQUUsQ0FBQztRQUNoQyxDQUFDO0tBQ0YsQ0FBQztBQUNKLENBQUM7QUFFRCxTQUFTLG1CQUFtQixDQUMxQixtQkFBaUMsRUFDakMsZ0JBQThCO0lBRTlCLElBQUksQ0FBQyxtQkFBbUIsSUFBSSxtQkFBbUIsS0FBSyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ3JFLE9BQU87WUFDTCxNQUFNLEVBQUUsZ0JBQWdCLElBQUksbUJBQW1CO1lBQy9DLE9BQU8sRUFBRSxHQUFHLEVBQUUsR0FBRSxDQUFDO1NBQ2xCLENBQUM7SUFDSixDQUFDO0lBQ0QsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUM7UUFDdEIsT0FBTztZQUNMLE1BQU0sRUFBRSxtQkFBbUI7WUFDM0IsT0FBTyxFQUFFLEdBQUcsRUFBRSxHQUFFLENBQUM7U0FDbEIsQ0FBQztJQUNKLENB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
|