wenay-common2 1.0.74 → 1.0.76
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/doc/ROADMAP.md +7 -6
- package/doc/changes/1.0.75.md +10 -0
- package/doc/changes/1.0.76.md +4 -0
- package/doc/wenay-common2-rare.md +38 -16
- package/doc/wenay-common2.md +438 -427
- package/lib/Common/events/replay-channel.js +55 -6
- package/lib/Common/events/replay-route.js +5 -2
- package/lib/Common/events/replay-wire.js +137 -47
- package/lib/Common/events/transport-lifecycle.d.ts +23 -0
- package/lib/Common/events/transport-lifecycle.js +94 -0
- package/lib/Common/rcp/rpc-client.js +346 -111
- package/lib/Common/rcp/rpc-clientHub.d.ts +2 -0
- package/lib/Common/rcp/rpc-clientHub.js +120 -25
- package/lib/Common/rcp/rpc-server.js +33 -9
- package/observe/store-manager.test.ts +43 -1
- package/oracle/realsocket/lifecycle.spec.ts +3 -3
- package/oracle/realsocket/replay-reconnect-auth.spec.ts +111 -0
- package/oracle/realsocket/replay-reconnect.spec.ts +592 -0
- package/package.json +1 -1
- package/replay/route-webrtc.test.ts +15 -2
- package/rpc.md +19 -4
- package/doc/changes/1.0.65.md +0 -8
- package/doc/changes/1.0.66.md +0 -8
|
@@ -9,6 +9,7 @@ const rpc_limits_1 = require("./rpc-limits");
|
|
|
9
9
|
const myThrow_1 = require("../../toError/myThrow");
|
|
10
10
|
const rpc_off_1 = require("./rpc-off");
|
|
11
11
|
const rpc_caps_1 = require("./rpc-caps");
|
|
12
|
+
const transport_lifecycle_1 = require("../events/transport-lifecycle");
|
|
12
13
|
const SHARED_POOLS = new WeakMap();
|
|
13
14
|
function sharedPool(socket, key) {
|
|
14
15
|
let byKey = SHARED_POOLS.get(socket);
|
|
@@ -53,6 +54,28 @@ function listenKeyArg(a) {
|
|
|
53
54
|
}
|
|
54
55
|
return a;
|
|
55
56
|
}
|
|
57
|
+
function createPathProxyCache() {
|
|
58
|
+
const values = new Map();
|
|
59
|
+
const registry = new FinalizationRegistry(function releaseProxy(entry) {
|
|
60
|
+
if (values.get(entry.key)?.token == entry.token)
|
|
61
|
+
values.delete(entry.key);
|
|
62
|
+
});
|
|
63
|
+
function get(path) {
|
|
64
|
+
const key = (0, rpc_path_1.rpcPathKey)(path);
|
|
65
|
+
const proxy = values.get(key)?.ref.deref();
|
|
66
|
+
if (!proxy)
|
|
67
|
+
values.delete(key);
|
|
68
|
+
return proxy;
|
|
69
|
+
}
|
|
70
|
+
function set(path, proxy) {
|
|
71
|
+
const key = (0, rpc_path_1.rpcPathKey)(path);
|
|
72
|
+
const token = {};
|
|
73
|
+
values.set(key, { ref: new WeakRef(proxy), token });
|
|
74
|
+
registry.register(proxy, { key, token });
|
|
75
|
+
return proxy;
|
|
76
|
+
}
|
|
77
|
+
return { get, set };
|
|
78
|
+
}
|
|
56
79
|
const IS_RPC_PIPE = Symbol.for("isRpcPipe");
|
|
57
80
|
function createClient(socket, key, opts) {
|
|
58
81
|
const limit = opts?.limit ?? 10000;
|
|
@@ -67,6 +90,7 @@ function createClient(socket, key, opts) {
|
|
|
67
90
|
const zombies = new Set();
|
|
68
91
|
const retire = (id) => { zombies.add(id); };
|
|
69
92
|
let disposed = false;
|
|
93
|
+
const transport = (0, transport_lifecycle_1.createTransportLifecycle)(true);
|
|
70
94
|
let disconnectCbs = [];
|
|
71
95
|
let disconnectResolve = () => { };
|
|
72
96
|
const disconnectPromise = new Promise(res => { disconnectResolve = res; });
|
|
@@ -77,6 +101,7 @@ function createClient(socket, key, opts) {
|
|
|
77
101
|
return (0, rpc_off_1.makeOff)(disconnectPromise, offDisconnect);
|
|
78
102
|
}
|
|
79
103
|
const routeCache = {};
|
|
104
|
+
let schemaKnown = false;
|
|
80
105
|
let strictData = {};
|
|
81
106
|
let strictWaiters = [];
|
|
82
107
|
let debug = false;
|
|
@@ -138,6 +163,8 @@ function createClient(socket, key, opts) {
|
|
|
138
163
|
break;
|
|
139
164
|
}
|
|
140
165
|
case rpc_protocol_1.Pkt.SHAPE: {
|
|
166
|
+
if (!callbacks.has(msg[1]))
|
|
167
|
+
break;
|
|
141
168
|
let m = compactShapes.get(msg[1]);
|
|
142
169
|
if (!m) {
|
|
143
170
|
m = [];
|
|
@@ -181,15 +208,17 @@ function createClient(socket, key, opts) {
|
|
|
181
208
|
break;
|
|
182
209
|
}
|
|
183
210
|
case rpc_protocol_1.Pkt.MAP: {
|
|
211
|
+
schemaKnown = true;
|
|
184
212
|
if (msg[1]) {
|
|
185
213
|
for (const k of Object.keys(routeCache))
|
|
186
214
|
delete routeCache[k];
|
|
187
215
|
Object.assign(routeCache, msg[1]);
|
|
188
216
|
}
|
|
189
217
|
if (Array.isArray(msg[3])) {
|
|
190
|
-
declaredListens
|
|
191
|
-
for (const
|
|
192
|
-
declaredListens.
|
|
218
|
+
declaredListens = new Set(msg[3]);
|
|
219
|
+
for (const sub of wireSubs.values()) {
|
|
220
|
+
sub.recoverable = declaredListens.has((0, rpc_path_1.rpcPathKey)(sub.path.slice(0, -1)));
|
|
221
|
+
}
|
|
193
222
|
}
|
|
194
223
|
if (msg[2]) {
|
|
195
224
|
for (const k of Object.keys(strictData))
|
|
@@ -212,7 +241,9 @@ function createClient(socket, key, opts) {
|
|
|
212
241
|
});
|
|
213
242
|
const sendPipe = (path, steps, wait) => {
|
|
214
243
|
if (disposed)
|
|
215
|
-
return wait ? Promise.reject(new Error(
|
|
244
|
+
return wait ? Promise.reject(new Error('RPC client disposed')) : Promise.resolve();
|
|
245
|
+
if (!transport.api.connected())
|
|
246
|
+
return wait ? Promise.reject(new Error('RPC transport disconnected')) : Promise.resolve();
|
|
216
247
|
const cbIds = [];
|
|
217
248
|
const cleanSteps = steps.map(step => {
|
|
218
249
|
if (step.type === 'call') {
|
|
@@ -225,15 +256,22 @@ function createClient(socket, key, opts) {
|
|
|
225
256
|
socket.emit(key, [rpc_protocol_1.Pkt.PIPE, 0, ref, cleanSteps, false]);
|
|
226
257
|
return Promise.resolve();
|
|
227
258
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
259
|
+
let record;
|
|
260
|
+
const promise = new Promise(function trackPipe(resolve, reject) {
|
|
261
|
+
if (pending.size >= limit) {
|
|
262
|
+
reject(new Error('RPC limit'));
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
231
265
|
const reqId = pool.next();
|
|
232
|
-
|
|
266
|
+
record = { ok: resolve, fail: reject, cbs: cbIds };
|
|
267
|
+
pending.set(reqId, record);
|
|
233
268
|
if (debug)
|
|
234
|
-
console.log(
|
|
269
|
+
console.log('[RPC PIPE]', path.join('.'), 'steps=', steps.length, 'id=', reqId);
|
|
235
270
|
socket.emit(key, [rpc_protocol_1.Pkt.PIPE, reqId, ref, cleanSteps]);
|
|
236
271
|
});
|
|
272
|
+
if (record)
|
|
273
|
+
record.promise = promise;
|
|
274
|
+
return promise;
|
|
237
275
|
};
|
|
238
276
|
const buildPipeProxy = (path, steps, wait) => {
|
|
239
277
|
const proxy = new Proxy(function () { }, {
|
|
@@ -276,134 +314,324 @@ function createClient(socket, key, opts) {
|
|
|
276
314
|
});
|
|
277
315
|
return proxy;
|
|
278
316
|
};
|
|
279
|
-
|
|
280
|
-
if (disposed)
|
|
281
|
-
return
|
|
317
|
+
function createCallAttempt(path, args) {
|
|
318
|
+
if (disposed) {
|
|
319
|
+
return { promise: Promise.reject(new Error('RPC client disposed')), abandon: function abandonDisposed() { } };
|
|
320
|
+
}
|
|
321
|
+
if (!transport.api.connected()) {
|
|
322
|
+
return { promise: Promise.reject(new Error('RPC transport disconnected')), abandon: function abandonOffline() { } };
|
|
323
|
+
}
|
|
324
|
+
if (pending.size >= limit) {
|
|
325
|
+
return { promise: Promise.reject(new Error('RPC limit')), abandon: function abandonLimited() { } };
|
|
326
|
+
}
|
|
282
327
|
const cbIds = [];
|
|
283
328
|
const clean = (0, rpc_walk_1.pack)(args, pool, callbacks, cbIds);
|
|
284
329
|
const ref = routeCache[(0, rpc_path_1.rpcPathKey)(path)] ?? path;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
if (pending.size >= limit)
|
|
291
|
-
return reject(new Error("RPC limit"));
|
|
292
|
-
const reqId = pool.next();
|
|
293
|
-
pending.set(reqId, { ok: resolve, fail: reject, cbs: cbIds });
|
|
294
|
-
if (debug)
|
|
295
|
-
console.log("[RPC]", path.join("."), "id=", reqId);
|
|
296
|
-
socket.emit(key, [rpc_protocol_1.Pkt.CALL, reqId, ref, clean]);
|
|
330
|
+
const reqId = pool.next();
|
|
331
|
+
let record;
|
|
332
|
+
const promise = new Promise(function trackCall(resolve, reject) {
|
|
333
|
+
record = { ok: resolve, fail: reject, cbs: cbIds };
|
|
334
|
+
pending.set(reqId, record);
|
|
297
335
|
});
|
|
298
|
-
|
|
336
|
+
record.promise = promise;
|
|
337
|
+
if (debug)
|
|
338
|
+
console.log('[RPC]', path.join('.'), 'id=', reqId);
|
|
339
|
+
socket.emit(key, [rpc_protocol_1.Pkt.CALL, reqId, ref, clean]);
|
|
340
|
+
function abandon(reason) {
|
|
341
|
+
if (pending.get(reqId) != record)
|
|
342
|
+
return;
|
|
343
|
+
pending.delete(reqId);
|
|
344
|
+
for (const cbId of cbIds) {
|
|
345
|
+
callbacks.delete(cbId);
|
|
346
|
+
compactShapes.delete(cbId);
|
|
347
|
+
}
|
|
348
|
+
record.fail(new Error(reason));
|
|
349
|
+
}
|
|
350
|
+
return { promise, abandon };
|
|
351
|
+
}
|
|
352
|
+
function sendCallWire(path, args, wait) {
|
|
353
|
+
if (disposed)
|
|
354
|
+
return wait ? Promise.reject(new Error('RPC client disposed')) : Promise.resolve();
|
|
355
|
+
if (!transport.api.connected())
|
|
356
|
+
return wait ? Promise.reject(new Error('RPC transport disconnected')) : Promise.resolve();
|
|
357
|
+
if (wait)
|
|
358
|
+
return createCallAttempt(path, args).promise;
|
|
359
|
+
const cbIds = [];
|
|
360
|
+
const clean = (0, rpc_walk_1.pack)(args, pool, callbacks, cbIds);
|
|
361
|
+
const ref = routeCache[(0, rpc_path_1.rpcPathKey)(path)] ?? path;
|
|
362
|
+
socket.emit(key, [rpc_protocol_1.Pkt.CALL, 0, ref, clean, false]);
|
|
363
|
+
return Promise.resolve();
|
|
364
|
+
}
|
|
299
365
|
const dedupe = opts?.dedupeListen ?? true;
|
|
300
366
|
const wireSubs = new Map();
|
|
301
367
|
let declaredListens = null;
|
|
368
|
+
function finishLogical(sub) {
|
|
369
|
+
if (sub.ended)
|
|
370
|
+
return;
|
|
371
|
+
sub.ended = true;
|
|
372
|
+
sub.attempt = null;
|
|
373
|
+
if (wireSubs.get(sub.key) == sub)
|
|
374
|
+
wireSubs.delete(sub.key);
|
|
375
|
+
for (const consumer of sub.consumers)
|
|
376
|
+
consumer.resolve();
|
|
377
|
+
sub.consumers.clear();
|
|
378
|
+
}
|
|
379
|
+
function finishAttempt(sub, attempt) {
|
|
380
|
+
if (sub.attempt != attempt)
|
|
381
|
+
return;
|
|
382
|
+
sub.attempt = null;
|
|
383
|
+
finishLogical(sub);
|
|
384
|
+
}
|
|
385
|
+
function startAttempt(sub) {
|
|
386
|
+
if (disposed || sub.ended || sub.attempt || sub.consumers.size == 0 || !transport.api.connected())
|
|
387
|
+
return;
|
|
388
|
+
const attempt = { call: createCallAttempt(sub.path, sub.realArgs) };
|
|
389
|
+
sub.attempt = attempt;
|
|
390
|
+
attempt.call.promise.then(function physicalListenEnded() { finishAttempt(sub, attempt); }, function physicalListenFailed() { finishAttempt(sub, attempt); });
|
|
391
|
+
}
|
|
392
|
+
function stopLogical(sub, socketAlive = transport.api.connected()) {
|
|
393
|
+
if (sub.ended)
|
|
394
|
+
return;
|
|
395
|
+
const attempt = sub.attempt;
|
|
396
|
+
sub.attempt = null;
|
|
397
|
+
if (wireSubs.get(sub.key) == sub)
|
|
398
|
+
wireSubs.delete(sub.key);
|
|
399
|
+
sub.ended = true;
|
|
400
|
+
if (socketAlive && transport.api.connected()) {
|
|
401
|
+
sendCallWire([...sub.path.slice(0, -1), 'removeCallback'], [], false);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
attempt?.call.abandon('RPC Listen stopped while transport is disconnected');
|
|
405
|
+
}
|
|
406
|
+
for (const consumer of sub.consumers)
|
|
407
|
+
consumer.resolve();
|
|
408
|
+
sub.consumers.clear();
|
|
409
|
+
}
|
|
302
410
|
function subscribeShared(path, args) {
|
|
303
411
|
if (disposed)
|
|
304
|
-
return Promise.reject(new Error(
|
|
412
|
+
return Promise.reject(new Error('RPC client disposed'));
|
|
305
413
|
const listenPathKey = (0, rpc_path_1.rpcPathKey)(path.slice(0, -1));
|
|
306
|
-
const skey = listenPathKey +
|
|
414
|
+
const skey = listenPathKey + '::' + JSON.stringify(args.map(listenKeyArg));
|
|
307
415
|
let sub = wireSubs.get(skey);
|
|
308
416
|
if (!sub) {
|
|
309
|
-
const created = { consumers: new Set(), stop: () => { } };
|
|
310
|
-
wireSubs.set(skey, created);
|
|
311
417
|
let fnPos = 0;
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
418
|
+
const created = {
|
|
419
|
+
key: skey,
|
|
420
|
+
path: [...path],
|
|
421
|
+
realArgs: [],
|
|
422
|
+
consumers: new Set(),
|
|
423
|
+
attempt: null,
|
|
424
|
+
recoverable: declaredListens?.has(listenPathKey) == true,
|
|
425
|
+
ended: false,
|
|
426
|
+
stop: function stopCreated(socketAlive) { stopLogical(created, socketAlive); },
|
|
427
|
+
};
|
|
428
|
+
created.realArgs = args.map(function buildMultiplexer(arg) {
|
|
429
|
+
if (typeof arg != 'function')
|
|
430
|
+
return arg;
|
|
431
|
+
const index = fnPos++;
|
|
432
|
+
return function multicastListenEvent(...event) {
|
|
433
|
+
let error;
|
|
434
|
+
for (const consumer of created.consumers) {
|
|
319
435
|
try {
|
|
320
|
-
|
|
436
|
+
consumer.fns[index]?.(...event);
|
|
321
437
|
}
|
|
322
|
-
catch (
|
|
323
|
-
|
|
438
|
+
catch (caught) {
|
|
439
|
+
error ??= caught;
|
|
324
440
|
}
|
|
325
|
-
}
|
|
326
|
-
if (
|
|
327
|
-
setTimeout(()
|
|
441
|
+
}
|
|
442
|
+
if (error != undefined)
|
|
443
|
+
setTimeout(function rethrowConsumerError() { throw error; }, 0);
|
|
328
444
|
};
|
|
329
445
|
});
|
|
330
|
-
|
|
331
|
-
if (wireSubs.get(skey) == created)
|
|
332
|
-
wireSubs.delete(skey);
|
|
333
|
-
created.consumers.forEach(c => c.resolve());
|
|
334
|
-
created.consumers.clear();
|
|
335
|
-
};
|
|
336
|
-
created.stop = () => {
|
|
337
|
-
sendCallWire([...path.slice(0, -1), "removeCallback"], [], false);
|
|
338
|
-
finish();
|
|
339
|
-
};
|
|
340
|
-
sendCallWire(path, realArgs, true).then(finish, finish);
|
|
446
|
+
wireSubs.set(skey, created);
|
|
341
447
|
sub = created;
|
|
342
448
|
}
|
|
343
|
-
const consumer = {
|
|
449
|
+
const consumer = {
|
|
450
|
+
fns: args.filter(a => typeof a == 'function'),
|
|
451
|
+
resolve: function resolveLater() { },
|
|
452
|
+
};
|
|
344
453
|
sub.consumers.add(consumer);
|
|
345
|
-
const
|
|
346
|
-
|
|
454
|
+
const promise = new Promise(function waitForListenEnd(resolve) { consumer.resolve = resolve; });
|
|
455
|
+
startAttempt(sub);
|
|
456
|
+
function unsubscribe() {
|
|
347
457
|
if (!sub.consumers.delete(consumer))
|
|
348
458
|
return;
|
|
349
459
|
consumer.resolve();
|
|
350
460
|
if (sub.consumers.size == 0)
|
|
351
461
|
sub.stop();
|
|
352
|
-
}
|
|
353
|
-
return (0, rpc_off_1.makeOff)(
|
|
462
|
+
}
|
|
463
|
+
return (0, rpc_off_1.makeOff)(promise, unsubscribe, { off: unsubscribe, unsubscribe, removeCallback: unsubscribe });
|
|
464
|
+
}
|
|
465
|
+
function abandonTransportGeneration(reason) {
|
|
466
|
+
for (const sub of [...wireSubs.values()]) {
|
|
467
|
+
const attempt = sub.attempt;
|
|
468
|
+
sub.attempt = null;
|
|
469
|
+
attempt?.call.abandon(reason);
|
|
470
|
+
if (!sub.recoverable)
|
|
471
|
+
finishLogical(sub);
|
|
472
|
+
}
|
|
473
|
+
const error = new Error(reason);
|
|
474
|
+
for (const [id, request] of [...pending]) {
|
|
475
|
+
pending.delete(id);
|
|
476
|
+
for (const cbId of request.cbs) {
|
|
477
|
+
callbacks.delete(cbId);
|
|
478
|
+
compactShapes.delete(cbId);
|
|
479
|
+
}
|
|
480
|
+
request.promise?.catch(function consumeAbandonedTransportReject() { });
|
|
481
|
+
request.fail(error);
|
|
482
|
+
}
|
|
483
|
+
callbacks.clear();
|
|
484
|
+
compactShapes.clear();
|
|
485
|
+
}
|
|
486
|
+
function transportDisconnected(reason) {
|
|
487
|
+
abandonTransportGeneration('RPC transport disconnected: ' + reason);
|
|
488
|
+
schemaKnown = false;
|
|
489
|
+
capsSent = false;
|
|
490
|
+
peerServerCaps = 0;
|
|
491
|
+
declaredListens = declaredListens ? new Set() : null;
|
|
492
|
+
for (const route of Object.keys(routeCache))
|
|
493
|
+
delete routeCache[route];
|
|
494
|
+
authStatus = undefined;
|
|
495
|
+
authPending = false;
|
|
496
|
+
const strict = strictWaiters;
|
|
497
|
+
strictWaiters = [];
|
|
498
|
+
for (const resolve of strict)
|
|
499
|
+
resolve(undefined);
|
|
500
|
+
const auths = authWaiters;
|
|
501
|
+
authWaiters = [];
|
|
502
|
+
for (const resolve of auths)
|
|
503
|
+
resolve({ ok: false, reason });
|
|
354
504
|
}
|
|
505
|
+
function transportConnected() {
|
|
506
|
+
for (const sub of [...wireSubs.values()]) {
|
|
507
|
+
if (sub.recoverable)
|
|
508
|
+
startAttempt(sub);
|
|
509
|
+
else
|
|
510
|
+
finishLogical(sub);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
transport.api.onDisconnect(transportDisconnected);
|
|
514
|
+
transport.api.onConnect(transportConnected);
|
|
355
515
|
const sendCall = (path, args, wait) => {
|
|
356
516
|
const last = path[path.length - 1];
|
|
357
517
|
if (dedupe && wait && path.length > 1 && (last == "callback" || last == "on") && args.some(a => typeof a == "function")) {
|
|
518
|
+
if (!transport.api.connected())
|
|
519
|
+
return sendCallWire(path, args, wait);
|
|
358
520
|
const isListen = declaredListens ? declaredListens.has((0, rpc_path_1.rpcPathKey)(path.slice(0, -1))) : true;
|
|
359
521
|
if (isListen)
|
|
360
522
|
return subscribeShared(path, args);
|
|
361
523
|
}
|
|
362
524
|
return sendCallWire(path, args, wait);
|
|
363
525
|
};
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
526
|
+
function lookupRpcMemberState(path, member) {
|
|
527
|
+
if (!schemaKnown)
|
|
528
|
+
return undefined;
|
|
529
|
+
const memberPath = [...path, member];
|
|
530
|
+
const memberKey = (0, rpc_path_1.rpcPathKey)(memberPath);
|
|
531
|
+
if (Object.prototype.hasOwnProperty.call(routeCache, memberKey))
|
|
532
|
+
return true;
|
|
533
|
+
if (declaredListens?.has(memberKey))
|
|
534
|
+
return true;
|
|
535
|
+
const target = resolveStrictTarget(memberPath);
|
|
536
|
+
if (target == 'dynamic')
|
|
537
|
+
return undefined;
|
|
538
|
+
return target != undefined && target != null && target != 'null';
|
|
539
|
+
}
|
|
540
|
+
function createRpcMemberLookup(path) {
|
|
541
|
+
const lookup = function lookupRpcMember(member) { return lookupRpcMemberState(path, member); };
|
|
542
|
+
Object.defineProperty(lookup, transport_lifecycle_1.RPC_MEMBER_LOOKUP, { value: true });
|
|
543
|
+
return lookup;
|
|
544
|
+
}
|
|
545
|
+
const proxyCaches = {
|
|
546
|
+
func: createPathProxyCache(),
|
|
547
|
+
space: createPathProxyCache(),
|
|
548
|
+
strict: createPathProxyCache(),
|
|
549
|
+
};
|
|
550
|
+
function buildProxy(path, wait, cache) {
|
|
551
|
+
const cached = cache.get(path);
|
|
552
|
+
if (cached)
|
|
553
|
+
return cached;
|
|
554
|
+
const proxy = new Proxy(function () { }, {
|
|
555
|
+
get(_, p) {
|
|
556
|
+
if (p == transport_lifecycle_1.RPC_MEMBER_LOOKUP)
|
|
557
|
+
return createRpcMemberLookup(path);
|
|
558
|
+
if (p == transport_lifecycle_1.RPC_TRANSPORT_LIFECYCLE)
|
|
559
|
+
return transport.api;
|
|
560
|
+
if (p == 'then' || p == 'catch' || p == Symbol.toPrimitive)
|
|
561
|
+
return undefined;
|
|
562
|
+
return buildProxy([...path, String(p)], wait, cache);
|
|
563
|
+
},
|
|
564
|
+
apply(_, __, args) {
|
|
565
|
+
const [fp, fa] = (0, rpc_walk_1.resolveCA)(path, args);
|
|
566
|
+
return sendCall(fp, fa, wait);
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
return cache.set(path, proxy);
|
|
570
|
+
}
|
|
571
|
+
function resolveStrictTarget(path) {
|
|
572
|
+
let target = strictData;
|
|
573
|
+
for (const segment of path) {
|
|
574
|
+
if (target == 'dynamic')
|
|
575
|
+
return target;
|
|
576
|
+
target = target?.[segment];
|
|
577
|
+
if (target == null || target == 'null')
|
|
380
578
|
return undefined;
|
|
381
|
-
if (tgt == "dynamic")
|
|
382
|
-
return buildProxy([...path], wait);
|
|
383
579
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
580
|
+
return target;
|
|
581
|
+
}
|
|
582
|
+
function buildStrict(path, wait) {
|
|
583
|
+
const initialTarget = resolveStrictTarget(path);
|
|
584
|
+
if (initialTarget == null || initialTarget == 'null')
|
|
585
|
+
return undefined;
|
|
586
|
+
const cached = proxyCaches.strict.get(path);
|
|
587
|
+
if (cached)
|
|
588
|
+
return cached;
|
|
589
|
+
const target = () => { };
|
|
590
|
+
const proxy = new Proxy(target, {
|
|
591
|
+
has: (_, p) => {
|
|
592
|
+
const target = resolveStrictTarget(path);
|
|
593
|
+
return target == 'dynamic' || (target?.[String(p)] != 'null' && target?.[String(p)] != undefined);
|
|
594
|
+
},
|
|
595
|
+
ownKeys: () => {
|
|
596
|
+
const target = resolveStrictTarget(path);
|
|
597
|
+
return target && typeof target == 'object' ? Object.keys(target) : [];
|
|
598
|
+
},
|
|
389
599
|
getOwnPropertyDescriptor: () => ({ enumerable: true, configurable: true }),
|
|
390
|
-
getPrototypeOf: () =>
|
|
600
|
+
getPrototypeOf: () => {
|
|
601
|
+
const target = resolveStrictTarget(path);
|
|
602
|
+
return target == 'func' || target == 'dynamic' ? Function.prototype : target ? null : Object.prototype;
|
|
603
|
+
},
|
|
391
604
|
get(_, p) {
|
|
392
|
-
if (p ==
|
|
605
|
+
if (p == transport_lifecycle_1.RPC_MEMBER_LOOKUP)
|
|
606
|
+
return createRpcMemberLookup(path);
|
|
607
|
+
if (p == transport_lifecycle_1.RPC_TRANSPORT_LIFECYCLE)
|
|
608
|
+
return transport.api;
|
|
609
|
+
if (p == 'then' || p == 'catch' || p == Symbol.toPrimitive)
|
|
393
610
|
return undefined;
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
611
|
+
const target = resolveStrictTarget(path);
|
|
612
|
+
if (p == 'call' && target == 'func') {
|
|
613
|
+
return function strictCall(_, ...args) { return sendCall(path, args, wait); };
|
|
614
|
+
}
|
|
615
|
+
if (target == 'func')
|
|
397
616
|
return undefined;
|
|
398
|
-
|
|
399
|
-
|
|
617
|
+
if (target != 'dynamic') {
|
|
618
|
+
const child = target?.[String(p)];
|
|
619
|
+
if (child == 'null' || child == undefined)
|
|
620
|
+
return undefined;
|
|
621
|
+
}
|
|
622
|
+
return buildStrict([...path, String(p)], wait);
|
|
400
623
|
},
|
|
401
624
|
apply(_, __, args) {
|
|
625
|
+
const target = resolveStrictTarget(path);
|
|
626
|
+
if (target != 'func' && target != 'dynamic') {
|
|
627
|
+
throw new TypeError('RPC strict path is not callable');
|
|
628
|
+
}
|
|
402
629
|
const [fp, fa] = (0, rpc_walk_1.resolveCA)(path, args);
|
|
403
630
|
return sendCall(fp, fa, wait);
|
|
404
631
|
},
|
|
405
632
|
});
|
|
406
|
-
|
|
633
|
+
return proxyCaches.strict.set(path, proxy);
|
|
634
|
+
}
|
|
407
635
|
const releaseCbs = (fn) => {
|
|
408
636
|
callbacks.forEach((cb, id) => { if (cb == fn) {
|
|
409
637
|
callbacks.delete(id);
|
|
@@ -411,40 +639,41 @@ function createClient(socket, key, opts) {
|
|
|
411
639
|
} });
|
|
412
640
|
};
|
|
413
641
|
function abortAll(reason) {
|
|
414
|
-
const err = { error: { name:
|
|
415
|
-
pending.forEach((p, id)
|
|
642
|
+
const err = { error: { name: 'RPC_ABORT', message: reason } };
|
|
643
|
+
pending.forEach(function abortPending(p, id) {
|
|
644
|
+
retire(id);
|
|
645
|
+
for (const cbId of p.cbs)
|
|
646
|
+
compactShapes.delete(cbId);
|
|
647
|
+
p.fail(err);
|
|
648
|
+
});
|
|
416
649
|
pending.clear();
|
|
417
|
-
callbacks.forEach((_, id)
|
|
650
|
+
callbacks.forEach(function abortCallback(_, id) { retire(id); });
|
|
418
651
|
callbacks.clear();
|
|
652
|
+
compactShapes.clear();
|
|
419
653
|
}
|
|
420
654
|
function drainWireSubs(socketAlive) {
|
|
421
|
-
const subs =
|
|
655
|
+
const subs = [...wireSubs.values()];
|
|
422
656
|
wireSubs.clear();
|
|
423
|
-
for (const
|
|
424
|
-
|
|
425
|
-
s.stop();
|
|
426
|
-
else {
|
|
427
|
-
s.consumers.forEach(c => c.resolve());
|
|
428
|
-
s.consumers.clear();
|
|
429
|
-
}
|
|
430
|
-
}
|
|
657
|
+
for (const sub of subs)
|
|
658
|
+
sub.stop(socketAlive);
|
|
431
659
|
}
|
|
432
|
-
function dispose(reason =
|
|
660
|
+
function dispose(reason = 'RPC client disposed', opts) {
|
|
433
661
|
if (disposed)
|
|
434
662
|
return;
|
|
435
663
|
const socketAlive = opts?.socketAlive ?? true;
|
|
436
|
-
abortAll(reason);
|
|
437
664
|
drainWireSubs(socketAlive);
|
|
665
|
+
abortAll(reason);
|
|
438
666
|
disposed = true;
|
|
667
|
+
transport.control.close(reason);
|
|
439
668
|
authPending = false;
|
|
440
669
|
const sw = strictWaiters;
|
|
441
670
|
strictWaiters = [];
|
|
442
|
-
for (const
|
|
443
|
-
|
|
671
|
+
for (const resolve of sw)
|
|
672
|
+
resolve(undefined);
|
|
444
673
|
const aw = authWaiters;
|
|
445
674
|
authWaiters = [];
|
|
446
|
-
for (const
|
|
447
|
-
|
|
675
|
+
for (const resolve of aw)
|
|
676
|
+
resolve({ ok: false, reason });
|
|
448
677
|
const dc = disconnectCbs;
|
|
449
678
|
disconnectCbs = [];
|
|
450
679
|
for (const cb of dc)
|
|
@@ -467,7 +696,9 @@ function createClient(socket, key, opts) {
|
|
|
467
696
|
end: releaseCbs,
|
|
468
697
|
subscriptions: () => Array.from(wireSubs, ([k, s]) => ({ key: k, consumers: s.consumers.size })),
|
|
469
698
|
};
|
|
470
|
-
const func = buildProxy([], true);
|
|
699
|
+
const func = buildProxy([], true, proxyCaches.func);
|
|
700
|
+
const space = buildProxy([], false, proxyCaches.space);
|
|
701
|
+
const strict = buildStrict([], true);
|
|
471
702
|
const pipe = buildPipeProxy([], [], true);
|
|
472
703
|
const pipeStrict = buildPipeProxy([], [], true);
|
|
473
704
|
let _ready = null;
|
|
@@ -475,6 +706,7 @@ function createClient(socket, key, opts) {
|
|
|
475
706
|
const init = async (obj) => {
|
|
476
707
|
if (obj) {
|
|
477
708
|
strictData = obj;
|
|
709
|
+
schemaKnown = true;
|
|
478
710
|
return;
|
|
479
711
|
}
|
|
480
712
|
if (authToken != null) {
|
|
@@ -486,7 +718,9 @@ function createClient(socket, key, opts) {
|
|
|
486
718
|
};
|
|
487
719
|
function reauth(token) {
|
|
488
720
|
if (disposed)
|
|
489
|
-
return Promise.resolve({ ok: false, reason:
|
|
721
|
+
return Promise.resolve({ ok: false, reason: 'RPC client disposed' });
|
|
722
|
+
if (!transport.api.connected())
|
|
723
|
+
return Promise.resolve({ ok: false, reason: 'RPC transport disconnected' });
|
|
490
724
|
authToken = token;
|
|
491
725
|
authPending = true;
|
|
492
726
|
socket.emit(key, [rpc_protocol_1.Pkt.HELLO, token]);
|
|
@@ -499,10 +733,11 @@ function createClient(socket, key, opts) {
|
|
|
499
733
|
func,
|
|
500
734
|
pipe,
|
|
501
735
|
pipeStrict,
|
|
502
|
-
space
|
|
736
|
+
space,
|
|
503
737
|
all: func,
|
|
504
|
-
strict
|
|
738
|
+
strict,
|
|
505
739
|
api,
|
|
740
|
+
[transport_lifecycle_1.RPC_TRANSPORT_CONTROL]: transport.control,
|
|
506
741
|
abortAll,
|
|
507
742
|
dispose,
|
|
508
743
|
close: dispose,
|
|
@@ -20,5 +20,7 @@ export declare function createRpcClientHub<T extends Record<string, RpcDescripto
|
|
|
20
20
|
readonly socket: T2;
|
|
21
21
|
onConnect: (func?: ((count: number) => void) | null) => void;
|
|
22
22
|
onDisconnect: (func?: ((reason: string) => void) | null) => void;
|
|
23
|
+
connectListen: (cb: (count: number) => void) => () => void;
|
|
24
|
+
disconnectListen: (cb: (reason: string) => void) => () => void;
|
|
23
25
|
connectCount: () => number;
|
|
24
26
|
};
|