wenay-common2 1.0.57 → 1.0.61
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/lib/Common/ObserveAll2/store-replay.d.ts +22 -11
- package/lib/Common/ObserveAll2/store-replay.js +27 -0
- package/lib/Common/ObserveAll2/store.d.ts +8 -0
- package/lib/Common/ObserveAll2/store.js +48 -0
- package/lib/Common/events/replay-conflate.d.ts +1 -0
- package/lib/Common/events/replay-conflate.js +1 -0
- package/lib/Common/events/replay-listen.d.ts +6 -1
- package/lib/Common/events/replay-listen.js +23 -3
- package/lib/Common/events/replay-wire.d.ts +7 -0
- package/lib/Common/events/replay-wire.js +45 -16
- package/lib/Common/rcp/listen-deep.d.ts +17 -3
- package/lib/Common/rcp/rpc-client.d.ts +6 -3
- package/lib/Common/rcp/rpc-server-auto.d.ts +9 -1
- package/lib/Common/rcp/rpc-server-auto.js +144 -7
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Store, StorePatch } from './store';
|
|
2
|
-
import { ReplayListenOptions } from '../events/replay-listen';
|
|
1
|
+
import { Store, StorePatch, StoreDrain, StoreEachCtx } from './store';
|
|
2
|
+
import { ReplayListenOptions, ReplayEvent } from '../events/replay-listen';
|
|
3
3
|
import { ReplayRemote, ReplaySubscribeOpts } from '../events/replay-wire';
|
|
4
4
|
import { ReplayStorage } from '../events/replay-history';
|
|
5
5
|
export type StoreReplayOpts = Pick<ReplayListenOptions<[StorePatch]>, 'history' | 'getSince' | 'onJournal' | 'now'>;
|
|
@@ -22,30 +22,31 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
|
|
|
22
22
|
isStale: () => boolean;
|
|
23
23
|
lastTs: () => number;
|
|
24
24
|
close: () => void;
|
|
25
|
-
getSince: (seq: number) =>
|
|
25
|
+
getSince: (seq: number) => ReplayEvent<[StorePatch]>[] | undefined;
|
|
26
26
|
line: {
|
|
27
|
-
func: import("../..").Listener<[
|
|
27
|
+
func: import("../..").Listener<[ReplayEvent<[StorePatch]>]>;
|
|
28
28
|
isRun: () => boolean;
|
|
29
29
|
run: () => void;
|
|
30
30
|
close: () => void;
|
|
31
31
|
eventClose: (cb: () => void) => () => void;
|
|
32
32
|
onClose: (cb: () => void) => () => void;
|
|
33
33
|
removeEventClose: (cb: () => void) => void;
|
|
34
|
-
on: import("../..").ListenOn<[
|
|
35
|
-
off: (k: (string | symbol) | import("../..").Listener<[
|
|
36
|
-
addListen: (cb: import("../..").Listener<[
|
|
34
|
+
on: import("../..").ListenOn<[ReplayEvent<[StorePatch]>]>;
|
|
35
|
+
off: (k: (string | symbol) | import("../..").Listener<[ReplayEvent<[StorePatch]>]> | null) => void;
|
|
36
|
+
addListen: (cb: import("../..").Listener<[ReplayEvent<[StorePatch]>]>, opts?: {
|
|
37
37
|
cbClose?: () => void;
|
|
38
38
|
key?: string | symbol;
|
|
39
39
|
}) => () => void;
|
|
40
|
-
removeListen: (k: (string | symbol) | import("../..").Listener<[
|
|
41
|
-
once: (cb: import("../..").Listener<[
|
|
40
|
+
removeListen: (k: (string | symbol) | import("../..").Listener<[ReplayEvent<[StorePatch]>]> | null) => void;
|
|
41
|
+
once: (cb: import("../..").Listener<[ReplayEvent<[StorePatch]>]>, opts?: {
|
|
42
42
|
key?: string | symbol;
|
|
43
43
|
}) => () => void;
|
|
44
44
|
count: () => number;
|
|
45
45
|
readonly getAllKeys: (string | symbol)[];
|
|
46
46
|
};
|
|
47
47
|
hasKeyframe: boolean;
|
|
48
|
-
keyframe: () =>
|
|
48
|
+
keyframe: () => ReplayEvent<[StorePatch]> | undefined;
|
|
49
|
+
frame: (sinceSeq: number, hint?: unknown) => ReplayEvent<[StorePatch]>[];
|
|
49
50
|
on: import("../events/replay-listen").ListenOnReplay<[StorePatch]>;
|
|
50
51
|
addListen: (cb: import("../..").Listener<[StorePatch]>, opts?: {
|
|
51
52
|
cbClose?: () => void;
|
|
@@ -54,7 +55,7 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
|
|
|
54
55
|
since?: number;
|
|
55
56
|
onSeq?: (seq: number) => void;
|
|
56
57
|
}) => () => void;
|
|
57
|
-
removeListen: (k: import("../..").Listener<[StorePatch]> |
|
|
58
|
+
removeListen: (k: (string | symbol) | import("../..").Listener<[StorePatch]> | null) => void;
|
|
58
59
|
once: (cb: import("../..").Listener<[StorePatch]>, opts?: {
|
|
59
60
|
key?: string | symbol;
|
|
60
61
|
current?: import("../..").ListenCurrent<[StorePatch]> | undefined;
|
|
@@ -76,6 +77,16 @@ export declare function syncStoreReplay<T extends object>(store: Store<T>, remot
|
|
|
76
77
|
isStale: () => boolean;
|
|
77
78
|
lastTs: () => number;
|
|
78
79
|
};
|
|
80
|
+
export declare function syncStoreReplayEach<T extends object>(remote: ReplayRemote<[StorePatch]>, cb: (key: string, value: T[keyof T] | undefined, ctx: StoreEachCtx) => void, opts?: ReplaySubscribeOpts & {
|
|
81
|
+
drain?: StoreDrain;
|
|
82
|
+
initial?: T;
|
|
83
|
+
}): (() => void) & {
|
|
84
|
+
store: Store<T>;
|
|
85
|
+
ready: Promise<void>;
|
|
86
|
+
seq: () => number;
|
|
87
|
+
isStale: () => boolean;
|
|
88
|
+
lastTs: () => number;
|
|
89
|
+
};
|
|
79
90
|
export declare function storeReplayAt<T extends object>(storage: ReplayStorage<[StorePatch]>, at?: {
|
|
80
91
|
seq?: number;
|
|
81
92
|
ts?: number;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.storePatchKey = storePatchKey;
|
|
4
4
|
exports.exposeStoreReplay = exposeStoreReplay;
|
|
5
5
|
exports.syncStoreReplay = syncStoreReplay;
|
|
6
|
+
exports.syncStoreReplayEach = syncStoreReplayEach;
|
|
6
7
|
exports.storeReplayAt = storeReplayAt;
|
|
7
8
|
const store_1 = require("./store");
|
|
8
9
|
const replay_listen_1 = require("../events/replay-listen");
|
|
@@ -21,9 +22,21 @@ function storePatchKey(patch) {
|
|
|
21
22
|
return null;
|
|
22
23
|
return JSON.stringify(patch.path);
|
|
23
24
|
}
|
|
25
|
+
function condensePatchTail(tail) {
|
|
26
|
+
const held = new Map();
|
|
27
|
+
for (const ev of tail) {
|
|
28
|
+
const k = storePatchKey(ev.event[0]);
|
|
29
|
+
if (k == null)
|
|
30
|
+
return tail;
|
|
31
|
+
held.delete(k);
|
|
32
|
+
held.set(k, ev);
|
|
33
|
+
}
|
|
34
|
+
return [...held.values()];
|
|
35
|
+
}
|
|
24
36
|
function exposeStoreReplay(store, opts = {}) {
|
|
25
37
|
const [emitPatch, lineApi] = (0, replay_listen_1.UseReplayListen)({
|
|
26
38
|
current: () => [{ path: [], exists: true, value: store.snapshot() }],
|
|
39
|
+
frame: condensePatchTail,
|
|
27
40
|
history: opts.getSince ? undefined : (opts.history ?? 1024),
|
|
28
41
|
getSince: opts.getSince,
|
|
29
42
|
onJournal: opts.onJournal,
|
|
@@ -42,6 +55,20 @@ function exposeStoreReplay(store, opts = {}) {
|
|
|
42
55
|
function syncStoreReplay(store, remote, opts = {}) {
|
|
43
56
|
return (0, replay_wire_1.replaySubscribe)(remote, function applyLine(patch) { (0, store_1.applyStorePatch)(store, patch); }, opts);
|
|
44
57
|
}
|
|
58
|
+
function syncStoreReplayEach(remote, cb, opts = {}) {
|
|
59
|
+
const { drain, initial, ...wireOpts } = opts;
|
|
60
|
+
const store = (0, store_1.createStore)((initial ?? {}), drain !== undefined ? { drain } : {});
|
|
61
|
+
const offEach = store.each().on(cb);
|
|
62
|
+
const sub = syncStoreReplay(store, remote, wireOpts);
|
|
63
|
+
function off() { offEach(); sub(); }
|
|
64
|
+
return Object.assign(off, {
|
|
65
|
+
store,
|
|
66
|
+
ready: sub.ready,
|
|
67
|
+
seq: sub.seq,
|
|
68
|
+
isStale: sub.isStale,
|
|
69
|
+
lastTs: sub.lastTs,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
45
72
|
function storeReplayAt(storage, at = {}) {
|
|
46
73
|
const envelopes = (0, replay_history_1.openHistory)(storage).at(at);
|
|
47
74
|
if (!envelopes)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { funcListenCallbackBase } from "../events/Listen";
|
|
1
2
|
import { listenUpdate, listenUpdatePaths, reactive, ReactiveChange } from "./reactive2";
|
|
2
3
|
export type StorePath = readonly PropertyKey[];
|
|
3
4
|
export type StoreDrain = "micro" | "immediate" | number | ((flush: () => void) => void);
|
|
@@ -67,6 +68,12 @@ export type StoreSelectionCtx<T, M> = {
|
|
|
67
68
|
mask: M;
|
|
68
69
|
paths: PropertyKey[][];
|
|
69
70
|
};
|
|
71
|
+
export type StoreEachOpts = {
|
|
72
|
+
depth?: number;
|
|
73
|
+
};
|
|
74
|
+
export type StoreEachCtx = {
|
|
75
|
+
path: PropertyKey[];
|
|
76
|
+
};
|
|
70
77
|
export type Store<T extends object> = {
|
|
71
78
|
readonly state: T;
|
|
72
79
|
readonly node: StoreNode<T>;
|
|
@@ -76,6 +83,7 @@ export type Store<T extends object> = {
|
|
|
76
83
|
on(cb: (value: T, ctx: StoreCtx<T>) => void, opts?: StoreSubOpts): () => void;
|
|
77
84
|
once(cb: (value: T, ctx: StoreCtx<T>) => void, opts?: StoreSubOpts): () => void;
|
|
78
85
|
update<M extends StoreMask<T>>(mask: M, opts?: StoreSubOpts): StoreSelection<T, M>;
|
|
86
|
+
each(opts?: StoreEachOpts): ReturnType<typeof funcListenCallbackBase<[key: string, value: T[keyof T] | undefined, ctx: StoreEachCtx]>>;
|
|
79
87
|
listen(): ReturnType<typeof listenUpdate>;
|
|
80
88
|
listenPaths(): ReturnType<typeof listenUpdatePaths>;
|
|
81
89
|
count(): number;
|
|
@@ -307,6 +307,48 @@ function createPatchesListen(store) {
|
|
|
307
307
|
},
|
|
308
308
|
});
|
|
309
309
|
}
|
|
310
|
+
function createEachListen(store, opts = {}) {
|
|
311
|
+
if (opts.depth != null && opts.depth != 1)
|
|
312
|
+
throw new Error("store.each: only depth 1 is supported (reserved option)");
|
|
313
|
+
return (0, Listen_1.funcListenCallbackBase)((emit) => {
|
|
314
|
+
const known = new Set(Reflect.ownKeys((0, reactive2_1.toRaw)(store.state)));
|
|
315
|
+
function emitKey(key) {
|
|
316
|
+
const raw = (0, reactive2_1.toRaw)(store.state);
|
|
317
|
+
const exists = isObj(raw) && key in raw;
|
|
318
|
+
if (exists)
|
|
319
|
+
known.add(key);
|
|
320
|
+
else
|
|
321
|
+
known.delete(key);
|
|
322
|
+
emit(key, exists ? store.state[key] : undefined, { path: [key] });
|
|
323
|
+
}
|
|
324
|
+
const off = store.listenPaths().on(function eachStoreChange(change) {
|
|
325
|
+
const keys = new Set();
|
|
326
|
+
let root = false;
|
|
327
|
+
for (const path of change.paths) {
|
|
328
|
+
if (path.length == 0)
|
|
329
|
+
root = true;
|
|
330
|
+
else
|
|
331
|
+
keys.add(path[0]);
|
|
332
|
+
}
|
|
333
|
+
if (root) {
|
|
334
|
+
for (const key of Reflect.ownKeys((0, reactive2_1.toRaw)(store.state)))
|
|
335
|
+
keys.add(key);
|
|
336
|
+
for (const key of known)
|
|
337
|
+
keys.add(key);
|
|
338
|
+
}
|
|
339
|
+
for (const key of keys)
|
|
340
|
+
emitKey(key);
|
|
341
|
+
});
|
|
342
|
+
return off;
|
|
343
|
+
}, {
|
|
344
|
+
event: (type, count, api) => {
|
|
345
|
+
if (type == "add" && count == 1 && !api.isRun())
|
|
346
|
+
api.run();
|
|
347
|
+
if (type == "remove" && count == 0 && api.isRun())
|
|
348
|
+
api.close();
|
|
349
|
+
},
|
|
350
|
+
});
|
|
351
|
+
}
|
|
310
352
|
function createChangedDataListen(store) {
|
|
311
353
|
return (0, Listen_1.funcListenCallbackBase)((emit) => {
|
|
312
354
|
const off = store.listenPaths().on((change) => {
|
|
@@ -458,6 +500,7 @@ function getNode(store, path) {
|
|
|
458
500
|
store._nodeCache.set(k, proxy);
|
|
459
501
|
return proxy;
|
|
460
502
|
}
|
|
503
|
+
let warnedRootOnEach = false;
|
|
461
504
|
function createSelection(store, base, mask, defaults = {}) {
|
|
462
505
|
const fullPaths = maskPaths(mask, base);
|
|
463
506
|
const rootNode = getNode(store, base);
|
|
@@ -486,6 +529,10 @@ function createSelection(store, base, mask, defaults = {}) {
|
|
|
486
529
|
return off;
|
|
487
530
|
},
|
|
488
531
|
onEach(cb, opts = {}) {
|
|
532
|
+
if (fullPaths.some(p => p.length == base.length) && !warnedRootOnEach) {
|
|
533
|
+
warnedRootOnEach = true;
|
|
534
|
+
console.warn("store: update(true).onEach fires ONCE per drain window with the WHOLE value (per selected path, not per key). For per-changed-key delivery use store.each(); for a subset — an explicit key mask.");
|
|
535
|
+
}
|
|
489
536
|
const o = { ...defaults, ...opts };
|
|
490
537
|
const offs = fullPaths.map(p => subscribePath(store, p, cb, o, false));
|
|
491
538
|
return () => { for (const off of offs)
|
|
@@ -508,6 +555,7 @@ function createStore(initial, opts = {}) {
|
|
|
508
555
|
on: (cb, opts) => getNode(store, []).on(cb, opts),
|
|
509
556
|
once: (cb, opts) => getNode(store, []).once(cb, opts),
|
|
510
557
|
update: (mask, opts) => createSelection(store, [], mask, opts),
|
|
558
|
+
each: (opts) => createEachListen(store, opts),
|
|
511
559
|
listen: () => (0, reactive2_1.listenUpdate)(state),
|
|
512
560
|
listenPaths: () => (0, reactive2_1.listenUpdatePaths)(state),
|
|
513
561
|
count: () => Array.from(store._counts.values()).reduce((a, b) => a + b, 0),
|
|
@@ -33,6 +33,7 @@ export declare function conflateReplay<T>(replay: ListenReplayApi<T>, opts: Conf
|
|
|
33
33
|
};
|
|
34
34
|
since: (seq: number) => ReplayEvent<NormalizeTuple<T>>[] | null;
|
|
35
35
|
keyframe: () => ReplayEvent<NormalizeTuple<T>> | null;
|
|
36
|
+
frame: (seq: number, hint?: unknown) => ReplayEvent<NormalizeTuple<T>>[];
|
|
36
37
|
};
|
|
37
38
|
close: () => void;
|
|
38
39
|
stats: () => {
|
|
@@ -95,6 +95,7 @@ function conflateReplay(replay, opts) {
|
|
|
95
95
|
line: gate,
|
|
96
96
|
since: (seq) => replay.getSince(seq) ?? null,
|
|
97
97
|
keyframe: () => replay.keyframe() ?? null,
|
|
98
|
+
frame: (seq, hint) => replay.frame(seq, hint),
|
|
98
99
|
},
|
|
99
100
|
close,
|
|
100
101
|
stats: () => ({ conflating, dropped, keyframes, coalesced, flushes }),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Listener, ListenApi, ListenCurrent, ListenCurrentProvider, ListenOnBrand, ListenOptions, NormalizeTuple } from './Listen3';
|
|
2
2
|
type key = string | symbol;
|
|
3
3
|
type cbClose = () => void;
|
|
4
|
+
export declare const IS_REPLAY_LISTEN: unique symbol;
|
|
5
|
+
export declare function isReplayListen(obj: any): obj is ListenReplayApi<any>;
|
|
4
6
|
export type ReplayEvent<Z extends any[] = any[]> = {
|
|
5
7
|
seq: number;
|
|
6
8
|
ts: number;
|
|
@@ -12,7 +14,8 @@ export type StaleInfo = {
|
|
|
12
14
|
age: number;
|
|
13
15
|
};
|
|
14
16
|
export type ReplayListenOptions<Z extends any[]> = {
|
|
15
|
-
current?: ListenCurrentProvider<Z
|
|
17
|
+
current?: ListenCurrentProvider<Z> | 'last';
|
|
18
|
+
frame?: (tail: ReplayEvent<Z>[], hint?: unknown) => ReplayEvent<Z>[];
|
|
16
19
|
history?: number;
|
|
17
20
|
getSince?: (seq: number) => ReplayEvent<Z>[] | undefined;
|
|
18
21
|
onJournal?: (ev: ReplayEvent<Z>) => void;
|
|
@@ -58,6 +61,7 @@ export declare function withReplayListen<T>(base: ListenApi<T>, options?: Replay
|
|
|
58
61
|
};
|
|
59
62
|
hasKeyframe: boolean;
|
|
60
63
|
keyframe: () => ReplayEvent<NormalizeTuple<T>> | undefined;
|
|
64
|
+
frame: (sinceSeq: number, hint?: unknown) => ReplayEvent<NormalizeTuple<T>>[];
|
|
61
65
|
on: ListenOnReplay<NormalizeTuple<T>>;
|
|
62
66
|
addListen: (cb: Listener<NormalizeTuple<T>>, opts?: ReplayOnOptions<NormalizeTuple<T>>) => () => void;
|
|
63
67
|
removeListen: (k: Listener<NormalizeTuple<T>> | null | key) => void;
|
|
@@ -106,6 +110,7 @@ export declare function UseReplayListen<T>(options?: ReplayListenUseOptions<T>):
|
|
|
106
110
|
};
|
|
107
111
|
hasKeyframe: boolean;
|
|
108
112
|
keyframe: () => ReplayEvent<NormalizeTuple<T>> | undefined;
|
|
113
|
+
frame: (sinceSeq: number, hint?: unknown) => ReplayEvent<NormalizeTuple<T>>[];
|
|
109
114
|
on: ListenOnReplay<NormalizeTuple<T>>;
|
|
110
115
|
addListen: (cb: Listener<NormalizeTuple<T>>, opts?: ReplayOnOptions<NormalizeTuple<T>>) => () => void;
|
|
111
116
|
removeListen: (k: key | Listener<NormalizeTuple<T>> | null) => void;
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IS_REPLAY_LISTEN = void 0;
|
|
4
|
+
exports.isReplayListen = isReplayListen;
|
|
3
5
|
exports.withReplayListen = withReplayListen;
|
|
4
6
|
exports.UseReplayListen = UseReplayListen;
|
|
5
7
|
const Listen3_1 = require("./Listen3");
|
|
8
|
+
exports.IS_REPLAY_LISTEN = Symbol.for('isReplayListen');
|
|
9
|
+
function isReplayListen(obj) {
|
|
10
|
+
return !!obj && typeof obj == 'object' && Object.prototype.hasOwnProperty.call(obj, exports.IS_REPLAY_LISTEN);
|
|
11
|
+
}
|
|
6
12
|
function withReplayListen(base, options = {}) {
|
|
7
|
-
const { current, history = 0, getSince, onJournal, now = Date.now, staleMs, onStale } = options;
|
|
13
|
+
const { current: currentOpt, frame: condense, history = 0, getSince, onJournal, now = Date.now, staleMs, onStale } = options;
|
|
14
|
+
let lastEv;
|
|
15
|
+
const current = currentOpt == 'last' ? () => lastEv?.event : currentOpt;
|
|
8
16
|
let head = 0;
|
|
9
17
|
const ring = [];
|
|
10
18
|
let emitting = null;
|
|
@@ -74,6 +82,8 @@ function withReplayListen(base, options = {}) {
|
|
|
74
82
|
const ev = { seq: ++head, ts: now(), event: e };
|
|
75
83
|
if (history > 0)
|
|
76
84
|
ring[(ev.seq - 1) % history] = ev;
|
|
85
|
+
if (currentOpt == 'last')
|
|
86
|
+
lastEv = ev;
|
|
77
87
|
onJournal?.(ev);
|
|
78
88
|
touchStale(ev.ts);
|
|
79
89
|
line.func(ev);
|
|
@@ -97,6 +107,15 @@ function withReplayListen(base, options = {}) {
|
|
|
97
107
|
const m = current?.();
|
|
98
108
|
return m ? { seq: head, ts: now(), event: m } : undefined;
|
|
99
109
|
},
|
|
110
|
+
frame: function frameSince(sinceSeq, hint) {
|
|
111
|
+
const tail = journalSince(sinceSeq);
|
|
112
|
+
if (tail)
|
|
113
|
+
return condense ? condense(tail, hint) : tail;
|
|
114
|
+
const m = current?.();
|
|
115
|
+
if (m)
|
|
116
|
+
return [{ seq: head, ts: now(), event: m }];
|
|
117
|
+
throw new Error(`replay frame(${sinceSeq}): journal evicted and no keyframe (sacred line)`);
|
|
118
|
+
},
|
|
100
119
|
on: ((cb, { cbClose, key, current: cur, since, onSeq } = {}) => {
|
|
101
120
|
if (since == null) {
|
|
102
121
|
const off = base.on(cb, { cbClose, key });
|
|
@@ -162,14 +181,15 @@ function withReplayListen(base, options = {}) {
|
|
|
162
181
|
},
|
|
163
182
|
get getAllKeys() { return base.getAllKeys; },
|
|
164
183
|
};
|
|
184
|
+
Object.defineProperty(api, exports.IS_REPLAY_LISTEN, { value: true });
|
|
165
185
|
(0, Listen3_1.registerListenOn)(api.on, api);
|
|
166
186
|
return api;
|
|
167
187
|
}
|
|
168
188
|
function UseReplayListen(options = {}) {
|
|
169
|
-
const { current, history, getSince, onJournal, now, staleMs, onStale, ...listenOptions } = options;
|
|
189
|
+
const { current, frame, history, getSince, onJournal, now, staleMs, onStale, ...listenOptions } = options;
|
|
170
190
|
let t;
|
|
171
191
|
const base = (0, Listen3_1.funcListenCallbackBase)((e) => { t = e; }, { fast: true, ...listenOptions });
|
|
172
|
-
const listen = withReplayListen(base, { current, history, getSince, onJournal, now, staleMs, onStale });
|
|
192
|
+
const listen = withReplayListen(base, { current, frame, history, getSince, onJournal, now, staleMs, onStale });
|
|
173
193
|
base.run();
|
|
174
194
|
t = listen.func;
|
|
175
195
|
return [t, listen];
|
|
@@ -5,6 +5,7 @@ export type ReplayExpose<T> = {
|
|
|
5
5
|
line: ListenReplayApi<T>['line'];
|
|
6
6
|
since: (seq: number) => ReplayEvent<NormalizeTuple<T>>[] | null;
|
|
7
7
|
keyframe: () => ReplayEvent<NormalizeTuple<T>> | null;
|
|
8
|
+
frame: ListenReplayApi<T>['frame'];
|
|
8
9
|
};
|
|
9
10
|
export declare function exposeReplay<T>(replay: ListenReplayApi<T>): ReplayExpose<T>;
|
|
10
11
|
export declare function exposeReplay<T>(replay: ListenReplayApi<T>, opts: {
|
|
@@ -19,6 +20,10 @@ export type ReplayRemote<Z extends any[] = any[]> = {
|
|
|
19
20
|
};
|
|
20
21
|
since: (seq: number) => Promise<ReplayEvent<Z>[] | null | undefined> | ReplayEvent<Z>[] | null | undefined;
|
|
21
22
|
keyframe: () => Promise<ReplayEvent<Z> | null | undefined> | ReplayEvent<Z> | null | undefined;
|
|
23
|
+
frame?: (seq: number, hint?: unknown) => Promise<ReplayEvent<Z>[] | null | undefined> | ReplayEvent<Z>[] | null | undefined;
|
|
24
|
+
frameLine?: {
|
|
25
|
+
on: (cb: (ev: ReplayEvent<Z>) => void) => any;
|
|
26
|
+
};
|
|
22
27
|
};
|
|
23
28
|
export type ReplaySubscribeOpts = {
|
|
24
29
|
since?: number;
|
|
@@ -28,6 +33,8 @@ export type ReplaySubscribeOpts = {
|
|
|
28
33
|
onStale?: (info: StaleInfo) => void;
|
|
29
34
|
skewMs?: number;
|
|
30
35
|
now?: () => number;
|
|
36
|
+
policy?: 'queue' | 'frame';
|
|
37
|
+
hint?: unknown;
|
|
31
38
|
};
|
|
32
39
|
export declare function replaySubscribe<Z extends any[]>(remote: ReplayRemote<Z>, cb: Listener<Z>, opts?: ReplaySubscribeOpts): (() => void) & {
|
|
33
40
|
ready: Promise<void>;
|
|
@@ -8,6 +8,7 @@ function exposeReplayPlain(replay) {
|
|
|
8
8
|
line: replay.line,
|
|
9
9
|
since: (seq) => replay.getSince(seq) ?? null,
|
|
10
10
|
keyframe: () => replay.keyframe() ?? null,
|
|
11
|
+
frame: (seq, hint) => replay.frame(seq, hint),
|
|
11
12
|
};
|
|
12
13
|
}
|
|
13
14
|
function exposeReplay(replay, opts) {
|
|
@@ -27,7 +28,7 @@ function unsubscribeHandle(handle) {
|
|
|
27
28
|
handle.unsubscribe();
|
|
28
29
|
}
|
|
29
30
|
function replaySubscribe(remote, cb, opts = {}) {
|
|
30
|
-
const { since = -1, onSeq, onError, staleMs, onStale, skewMs = 0, now = Date.now } = opts;
|
|
31
|
+
const { since = -1, onSeq, onError, staleMs, onStale, skewMs = 0, now = Date.now, policy = 'queue', hint } = opts;
|
|
31
32
|
let lastDelivered = since;
|
|
32
33
|
let replaying = true;
|
|
33
34
|
let closed = false;
|
|
@@ -94,7 +95,19 @@ function replaySubscribe(remote, cb, opts = {}) {
|
|
|
94
95
|
if (!replaying)
|
|
95
96
|
assessStale();
|
|
96
97
|
}
|
|
97
|
-
const
|
|
98
|
+
const liveLine = policy == 'frame' && remote.frameLine ? remote.frameLine : remote.line;
|
|
99
|
+
const handle = liveLine.on(function liveTap(ev) {
|
|
100
|
+
if (ev == null || typeof ev.seq != 'number') {
|
|
101
|
+
if (closed)
|
|
102
|
+
return;
|
|
103
|
+
const err = new Error('replaySubscribe: line ended by server (' + String(ev) + ')');
|
|
104
|
+
off();
|
|
105
|
+
if (onError)
|
|
106
|
+
onError(err);
|
|
107
|
+
else
|
|
108
|
+
setTimeout(function rethrowLineEnd() { throw err; }, 0);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
98
111
|
lastArrival = now();
|
|
99
112
|
if (replaying)
|
|
100
113
|
queue.push(ev);
|
|
@@ -103,23 +116,39 @@ function replaySubscribe(remote, cb, opts = {}) {
|
|
|
103
116
|
});
|
|
104
117
|
async function catchUp() {
|
|
105
118
|
try {
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
119
|
+
let done = false;
|
|
120
|
+
if (since >= 0 && remote.frame) {
|
|
121
|
+
const envs = await remote.frame(since, hint);
|
|
122
|
+
if (closed)
|
|
123
|
+
return;
|
|
124
|
+
if (envs) {
|
|
125
|
+
if (envs.length) {
|
|
126
|
+
lastDelivered = envs[0].seq - 1;
|
|
127
|
+
for (const ev of envs)
|
|
128
|
+
deliver(ev);
|
|
129
|
+
}
|
|
130
|
+
done = true;
|
|
131
|
+
}
|
|
112
132
|
}
|
|
113
|
-
|
|
114
|
-
const
|
|
133
|
+
if (!done) {
|
|
134
|
+
const tail = since >= 0 ? await remote.since(since) : null;
|
|
115
135
|
if (closed)
|
|
116
136
|
return;
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
137
|
+
if (tail) {
|
|
138
|
+
for (const ev of tail)
|
|
139
|
+
deliver(ev);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
const kf = await remote.keyframe();
|
|
143
|
+
if (closed)
|
|
144
|
+
return;
|
|
145
|
+
if (kf) {
|
|
146
|
+
lastDelivered = kf.seq;
|
|
147
|
+
lastTs = kf.ts;
|
|
148
|
+
lastArrival = now();
|
|
149
|
+
cb(...kf.event);
|
|
150
|
+
onSeq?.(kf.seq);
|
|
151
|
+
}
|
|
123
152
|
}
|
|
124
153
|
}
|
|
125
154
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ListenOn } from "../events/Listen";
|
|
2
|
+
import type { ReplayEvent } from "../events/replay-listen";
|
|
2
3
|
import { listenSocket, listenSocketFirst, listenSocketAll, listenSocketSmart, type tSubHandle } from "./listen-socket";
|
|
3
4
|
type WithSubHandle<R> = R extends {
|
|
4
5
|
callback: (...a: infer A) => any;
|
|
@@ -8,11 +9,24 @@ type WithSubHandle<R> = R extends {
|
|
|
8
9
|
once: (...a: A) => tSubHandle & Promise<void>;
|
|
9
10
|
} : R;
|
|
10
11
|
type Obj = Record<string, any>;
|
|
11
|
-
type InferArgs<T> = T extends {
|
|
12
|
+
export type InferArgs<T> = T extends {
|
|
12
13
|
addListen: (cb: (...args: infer R) => void, ...rest: any[]) => any;
|
|
13
14
|
} ? R : never;
|
|
15
|
+
export type ReplaySocketListen<Z extends any[]> = WithSubHandle<ReturnType<typeof listenSocket<Z>>> & {
|
|
16
|
+
line: WithSubHandle<ReturnType<typeof listenSocket<[ReplayEvent<Z>]>>>;
|
|
17
|
+
frameLine: WithSubHandle<ReturnType<typeof listenSocket<[ReplayEvent<Z>]>>>;
|
|
18
|
+
since: (seq: number) => Promise<ReplayEvent<Z>[] | null>;
|
|
19
|
+
keyframe: () => Promise<ReplayEvent<Z> | null>;
|
|
20
|
+
frame: (seq: number, hint?: unknown) => Promise<ReplayEvent<Z>[]>;
|
|
21
|
+
};
|
|
22
|
+
export type IsReplayMember<V> = V extends {
|
|
23
|
+
addListen: Function;
|
|
24
|
+
getSince: Function;
|
|
25
|
+
keyframe: Function;
|
|
26
|
+
line: object;
|
|
27
|
+
} ? true : false;
|
|
14
28
|
export type DeepSocketListen<T> = {
|
|
15
|
-
[K in keyof T]: T[K] extends {
|
|
29
|
+
[K in keyof T]: IsReplayMember<T[K]> extends true ? ReplaySocketListen<InferArgs<T[K]>> : T[K] extends {
|
|
16
30
|
addListen: Function;
|
|
17
31
|
} ? WithSubHandle<ReturnType<typeof listenSocket<InferArgs<T[K]>>>> : T[K] extends ListenOn<infer Z> ? WithSubHandle<ReturnType<typeof listenSocket<Z>>> : T[K] extends (...a: any[]) => any ? T[K] : T[K] extends Promise<any> ? T[K] : T[K] extends typeof Promise ? T[K] : T[K] extends object ? DeepSocketListen<T[K]> : T[K];
|
|
18
32
|
};
|
|
@@ -27,7 +41,7 @@ export type DeepSocketListenAll<T> = {
|
|
|
27
41
|
} ? ReturnType<typeof listenSocketAll<InferArgs<T[K]>>> : T[K] extends ListenOn<infer Z> ? ReturnType<typeof listenSocketAll<Z>> : T[K] extends (...a: any[]) => any ? T[K] : T[K] extends Promise<any> ? T[K] : T[K] extends typeof Promise ? T[K] : T[K] extends object ? DeepSocketListenAll<T[K]> : T[K];
|
|
28
42
|
};
|
|
29
43
|
export type DeepSocketListenSmart<T> = {
|
|
30
|
-
[K in keyof T]: NonNullable<T[K]> extends {
|
|
44
|
+
[K in keyof T]: IsReplayMember<NonNullable<T[K]>> extends true ? ReplaySocketListen<InferArgs<NonNullable<T[K]>>> | Extract<T[K], undefined | null> : NonNullable<T[K]> extends {
|
|
31
45
|
addListen: Function;
|
|
32
46
|
} ? ReturnType<typeof listenSocketSmart<InferArgs<NonNullable<T[K]>>>> | Extract<T[K], undefined | null> : NonNullable<T[K]> extends ListenOn<infer Z> ? ReturnType<typeof listenSocketSmart<Z>> : NonNullable<T[K]> extends (...a: any[]) => any ? T[K] : NonNullable<T[K]> extends Promise<any> ? T[K] : NonNullable<T[K]> extends typeof Promise ? T[K] : NonNullable<T[K]> extends object ? DeepSocketListenSmart<T[K]> : T[K];
|
|
33
47
|
};
|
|
@@ -2,16 +2,19 @@ import { type SocketTmpl } from "./rpc-protocol";
|
|
|
2
2
|
import { type RpcLimits } from "./rpc-limits";
|
|
3
3
|
import { makeOff } from "./rpc-off";
|
|
4
4
|
import { type RpcOpt } from "./rpc-caps";
|
|
5
|
+
import type { IsReplayMember, InferArgs, ReplaySocketListen } from "./listen-deep";
|
|
5
6
|
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
|
|
6
|
-
export type DeepDataOnly<T> = T extends Function ? never : T extends
|
|
7
|
+
export type DeepDataOnly<T> = T extends Function ? never : T extends readonly any[] ? {
|
|
8
|
+
[I in keyof T]: DeepDataOnly<T[I]>;
|
|
9
|
+
} : T extends object ? {
|
|
7
10
|
[K in keyof T as T[K] extends Function ? never : K]: DeepDataOnly<T[K]>;
|
|
8
11
|
} : T;
|
|
9
12
|
export type ClientAPIAll<T> = {
|
|
10
|
-
[K in keyof T as T[K] extends Function ? K : T[K] extends object ? K : never]: T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<DeepDataOnly<UnwrapPromise<R>>> : T[K] extends object ? ClientAPIAll<T[K]> : never;
|
|
13
|
+
[K in keyof T as T[K] extends Function ? K : T[K] extends object ? K : never]: IsReplayMember<T[K]> extends true ? ReplaySocketListen<InferArgs<T[K]>> : T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<DeepDataOnly<UnwrapPromise<R>>> : T[K] extends object ? ClientAPIAll<T[K]> : never;
|
|
11
14
|
};
|
|
12
15
|
type NonFalsy<T> = Exclude<T, false | null | 0 | "" | undefined>;
|
|
13
16
|
export type ClientAPIStrict<T> = {
|
|
14
|
-
[K in keyof T as NonFalsy<T[K]> extends Function ? K : NonFalsy<T[K]> extends object ? K : never]: NonFalsy<T[K]> extends (...args: infer A) => infer R ? (...args: A) => Promise<DeepDataOnly<UnwrapPromise<R>>> : NonFalsy<T[K]> extends object ? ClientAPIStrict<NonFalsy<T[K]>> : never;
|
|
17
|
+
[K in keyof T as NonFalsy<T[K]> extends Function ? K : NonFalsy<T[K]> extends object ? K : never]: IsReplayMember<NonFalsy<T[K]>> extends true ? ReplaySocketListen<InferArgs<NonFalsy<T[K]>>> : NonFalsy<T[K]> extends (...args: infer A) => infer R ? (...args: A) => Promise<DeepDataOnly<UnwrapPromise<R>>> : NonFalsy<T[K]> extends object ? ClientAPIStrict<NonFalsy<T[K]>> : never;
|
|
15
18
|
};
|
|
16
19
|
export interface PipeArrayAPI<T> extends Promise<DeepDataOnly<T[]>> {
|
|
17
20
|
[index: number]: PipeAPI<T>;
|
|
@@ -3,7 +3,13 @@ import { type PromiseServerHooks, type RpcLimits, type RpcServerAuth, type RpcOp
|
|
|
3
3
|
import { DeepSocketListen } from "./listen-deep";
|
|
4
4
|
import { SocketTmpl } from "./rpc-protocol";
|
|
5
5
|
type ListenCallbackBase<T extends any[] = any[]> = ReturnType<typeof funcListenCallbackBase<T>>;
|
|
6
|
-
export
|
|
6
|
+
export type RpcReplayOpts = {
|
|
7
|
+
pending?: () => number;
|
|
8
|
+
highWater?: number;
|
|
9
|
+
lowWater?: number;
|
|
10
|
+
pollMs?: number;
|
|
11
|
+
};
|
|
12
|
+
export declare function createRpcServerAuto<T extends object>({ socket, object: target, socketKey: key, debug, hooks, disconnectListen, limits, auth, maxPerListen, throttle, opt, replay, replayOpts }: {
|
|
7
13
|
socket: SocketTmpl;
|
|
8
14
|
object: T;
|
|
9
15
|
socketKey: string;
|
|
@@ -15,6 +21,8 @@ export declare function createRpcServerAuto<T extends object>({ socket, object:
|
|
|
15
21
|
maxPerListen?: number;
|
|
16
22
|
throttle?: number;
|
|
17
23
|
opt?: RpcOpt;
|
|
24
|
+
replay?: false | "auto" | "force";
|
|
25
|
+
replayOpts?: RpcReplayOpts;
|
|
18
26
|
}): {
|
|
19
27
|
api: {
|
|
20
28
|
subscriptions: () => {
|
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createRpcServerAuto = createRpcServerAuto;
|
|
4
4
|
const Listen_1 = require("../events/Listen");
|
|
5
|
+
const replay_listen_1 = require("../events/replay-listen");
|
|
5
6
|
const listen_socket_1 = require("./listen-socket");
|
|
6
7
|
const rpc_server_1 = require("./rpc-server");
|
|
7
8
|
const rpc_protocol_1 = require("./rpc-protocol");
|
|
8
|
-
function createRpcServerAuto({ socket, object: target, socketKey: key, debug, hooks, disconnectListen, limits, auth, maxPerListen, throttle, opt }) {
|
|
9
|
+
function createRpcServerAuto({ socket, object: target, socketKey: key, debug, hooks, disconnectListen, limits, auth, maxPerListen, throttle, opt, replay = "auto", replayOpts }) {
|
|
9
10
|
const cache = new WeakMap();
|
|
10
11
|
const registry = new Map();
|
|
11
12
|
function unsubscribeAllActive() {
|
|
@@ -15,7 +16,8 @@ function createRpcServerAuto({ socket, object: target, socketKey: key, debug, ho
|
|
|
15
16
|
}
|
|
16
17
|
registry.clear();
|
|
17
18
|
}
|
|
18
|
-
function getListenSocket(parent, disconnectListen) {
|
|
19
|
+
function getListenSocket(parent, disconnectListen, nodeOpt) {
|
|
20
|
+
const nodeThrottle = nodeOpt ? nodeOpt.throttle : throttle;
|
|
19
21
|
let result = cache.get(parent);
|
|
20
22
|
if (!result) {
|
|
21
23
|
const subs = new Map();
|
|
@@ -27,7 +29,7 @@ function createRpcServerAuto({ socket, object: target, socketKey: key, debug, ho
|
|
|
27
29
|
if (!registry.has(parent))
|
|
28
30
|
registry.set(parent, { subs });
|
|
29
31
|
subs.get(z)?.off();
|
|
30
|
-
const w = (0, listen_socket_1.listenSocket)(parent, { addListenClose: disconnectListen, throttle });
|
|
32
|
+
const w = (0, listen_socket_1.listenSocket)(parent, { addListenClose: disconnectListen, throttle: nodeThrottle });
|
|
31
33
|
subs.set(z, w);
|
|
32
34
|
const done = w.on(z);
|
|
33
35
|
done.then(() => {
|
|
@@ -46,7 +48,7 @@ function createRpcServerAuto({ socket, object: target, socketKey: key, debug, ho
|
|
|
46
48
|
if (!registry.has(parent))
|
|
47
49
|
registry.set(parent, { subs });
|
|
48
50
|
subs.get(z)?.off();
|
|
49
|
-
const w = (0, listen_socket_1.listenSocket)(parent, { addListenClose: disconnectListen, throttle });
|
|
51
|
+
const w = (0, listen_socket_1.listenSocket)(parent, { addListenClose: disconnectListen, throttle: nodeThrottle });
|
|
50
52
|
let fired = false;
|
|
51
53
|
const oneShot = (...a) => {
|
|
52
54
|
if (fired)
|
|
@@ -79,6 +81,137 @@ function createRpcServerAuto({ socket, object: target, socketKey: key, debug, ho
|
|
|
79
81
|
}
|
|
80
82
|
return result;
|
|
81
83
|
}
|
|
84
|
+
function isReplayNode(obj) {
|
|
85
|
+
if (replay == false || obj == null || typeof obj != "object")
|
|
86
|
+
return false;
|
|
87
|
+
if (Object.prototype.hasOwnProperty.call(obj, replay_listen_1.IS_REPLAY_LISTEN))
|
|
88
|
+
return true;
|
|
89
|
+
return replay == "force"
|
|
90
|
+
&& (0, Listen_1.isListenCallback)(obj)
|
|
91
|
+
&& typeof obj.getSince == "function"
|
|
92
|
+
&& typeof obj.keyframe == "function"
|
|
93
|
+
&& !!obj.line && typeof obj.line == "object";
|
|
94
|
+
}
|
|
95
|
+
function lineFrame(parent, seq, hint) {
|
|
96
|
+
if (typeof parent.frame == "function")
|
|
97
|
+
return parent.frame(seq, hint);
|
|
98
|
+
const tail = parent.getSince(seq);
|
|
99
|
+
if (tail)
|
|
100
|
+
return tail;
|
|
101
|
+
const kf = parent.keyframe();
|
|
102
|
+
if (kf)
|
|
103
|
+
return [kf];
|
|
104
|
+
throw new Error(`replay frame(${seq}): journal evicted and no keyframe (sacred line)`);
|
|
105
|
+
}
|
|
106
|
+
const gateClosers = new Set();
|
|
107
|
+
function closeAllGates() { for (const c of [...gateClosers])
|
|
108
|
+
c(); }
|
|
109
|
+
let gatesHooked = false;
|
|
110
|
+
function hookGateTeardown() {
|
|
111
|
+
if (gatesHooked || !disconnectListen)
|
|
112
|
+
return;
|
|
113
|
+
gatesHooked = true;
|
|
114
|
+
const dl = disconnectListen;
|
|
115
|
+
if (typeof dl.on == "function")
|
|
116
|
+
dl.on(closeAllGates);
|
|
117
|
+
else
|
|
118
|
+
dl.addListen(closeAllGates);
|
|
119
|
+
}
|
|
120
|
+
function gatedLineNode(parent) {
|
|
121
|
+
const { pending: pendingOpt, highWater = Infinity, lowWater = 0, pollMs = 25 } = replayOpts ?? {};
|
|
122
|
+
const pending = pendingOpt ?? (() => socket?.conn?.writeBuffer?.length ?? 0);
|
|
123
|
+
const out = (0, Listen_1.funcListenCallbackBase)(() => { });
|
|
124
|
+
out.run();
|
|
125
|
+
let lastSent = typeof parent.head == "function" ? parent.head() : 0;
|
|
126
|
+
let gated = false;
|
|
127
|
+
let closed = false;
|
|
128
|
+
let timer = null;
|
|
129
|
+
function stopPoll() { if (timer) {
|
|
130
|
+
clearInterval(timer);
|
|
131
|
+
timer = null;
|
|
132
|
+
} }
|
|
133
|
+
function startPoll() {
|
|
134
|
+
if (timer || closed)
|
|
135
|
+
return;
|
|
136
|
+
timer = setInterval(recoverIfDrained, pollMs);
|
|
137
|
+
timer.unref?.();
|
|
138
|
+
}
|
|
139
|
+
function close() {
|
|
140
|
+
if (closed)
|
|
141
|
+
return;
|
|
142
|
+
closed = true;
|
|
143
|
+
stopPoll();
|
|
144
|
+
offLine();
|
|
145
|
+
gateClosers.delete(close);
|
|
146
|
+
}
|
|
147
|
+
function fail(e) {
|
|
148
|
+
if (debug)
|
|
149
|
+
console.error("[rpc replay gate] frame recovery failed:", e);
|
|
150
|
+
const emitStop = !closed;
|
|
151
|
+
close();
|
|
152
|
+
if (emitStop)
|
|
153
|
+
out.func(rpc_protocol_1.RPC_STOP);
|
|
154
|
+
out.close?.();
|
|
155
|
+
}
|
|
156
|
+
function recoverIfDrained() {
|
|
157
|
+
if (!gated || closed)
|
|
158
|
+
return;
|
|
159
|
+
if (pending() > lowWater)
|
|
160
|
+
return;
|
|
161
|
+
gated = false;
|
|
162
|
+
stopPoll();
|
|
163
|
+
let envs;
|
|
164
|
+
try {
|
|
165
|
+
envs = lineFrame(parent, lastSent);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
fail(e);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
for (const ev of envs) {
|
|
172
|
+
if (ev.seq > lastSent)
|
|
173
|
+
lastSent = ev.seq;
|
|
174
|
+
out.func(ev);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const offLine = parent.line.on(function gateForward(ev) {
|
|
178
|
+
if (closed)
|
|
179
|
+
return;
|
|
180
|
+
if (!gated && pending() > highWater) {
|
|
181
|
+
gated = true;
|
|
182
|
+
startPoll();
|
|
183
|
+
}
|
|
184
|
+
if (gated) {
|
|
185
|
+
recoverIfDrained();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
lastSent = ev.seq;
|
|
189
|
+
out.func(ev);
|
|
190
|
+
});
|
|
191
|
+
gateClosers.add(close);
|
|
192
|
+
hookGateTeardown();
|
|
193
|
+
return getListenSocket(out, disconnectListen, { throttle: undefined });
|
|
194
|
+
}
|
|
195
|
+
const replayCache = new WeakMap();
|
|
196
|
+
function getReplayExpose(parent) {
|
|
197
|
+
let node = replayCache.get(parent);
|
|
198
|
+
if (node)
|
|
199
|
+
return node;
|
|
200
|
+
const legacy = getListenSocket(parent, disconnectListen);
|
|
201
|
+
const lineNode = getListenSocket(parent.line, disconnectListen, { throttle: undefined });
|
|
202
|
+
const frameLineNode = replayOpts?.highWater != null ? gatedLineNode(parent) : lineNode;
|
|
203
|
+
node = {
|
|
204
|
+
...legacy,
|
|
205
|
+
line: lineNode,
|
|
206
|
+
frameLine: frameLineNode,
|
|
207
|
+
since: (seq) => parent.getSince(seq) ?? null,
|
|
208
|
+
keyframe: () => parent.keyframe() ?? null,
|
|
209
|
+
frame: (seq, hint) => lineFrame(parent, seq, hint),
|
|
210
|
+
};
|
|
211
|
+
node[rpc_protocol_1.IS_RPC_LISTEN] = true;
|
|
212
|
+
replayCache.set(parent, node);
|
|
213
|
+
return node;
|
|
214
|
+
}
|
|
82
215
|
const api = {
|
|
83
216
|
subscriptions: () => Array.from(registry, ([parent, e], i) => ({
|
|
84
217
|
key: parent?.constructor?.name ? `${parent.constructor.name}#${i}` : `listen#${i}`,
|
|
@@ -89,12 +222,16 @@ function createRpcServerAuto({ socket, object: target, socketKey: key, debug, ho
|
|
|
89
222
|
socket, object: target, socketKey: key, debug, limits, auth, opt,
|
|
90
223
|
hooks: {
|
|
91
224
|
...hooks,
|
|
92
|
-
onDispose: () => { unsubscribeAllActive(); hooks?.onDispose?.(); },
|
|
225
|
+
onDispose: () => { closeAllGates(); unsubscribeAllActive(); hooks?.onDispose?.(); },
|
|
93
226
|
resolveTransform: (obj) => {
|
|
227
|
+
if (isReplayNode(obj))
|
|
228
|
+
return getReplayExpose(obj);
|
|
94
229
|
if ((0, Listen_1.isListenCallback)(obj))
|
|
95
230
|
return getListenSocket(obj, disconnectListen);
|
|
96
|
-
if ((0, Listen_1.isListenOn)(obj))
|
|
97
|
-
|
|
231
|
+
if ((0, Listen_1.isListenOn)(obj)) {
|
|
232
|
+
const byOn = (0, Listen_1.getListenByOn)(obj);
|
|
233
|
+
return isReplayNode(byOn) ? getReplayExpose(byOn) : getListenSocket(byOn, disconnectListen);
|
|
234
|
+
}
|
|
98
235
|
return obj;
|
|
99
236
|
},
|
|
100
237
|
},
|