wenay-common2 1.0.55 → 1.0.57
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/reactive2.js +47 -17
- package/lib/Common/ObserveAll2/store-replay.d.ts +6 -26
- package/lib/Common/ObserveAll2/store.js +3 -3
- package/lib/Common/events/replay-conflate.js +5 -2
- package/lib/Common/events/replay-listen.d.ts +13 -2
- package/lib/Common/events/replay-listen.js +45 -3
- package/lib/Common/events/replay-wire.d.ts +20 -26
- package/lib/Common/events/replay-wire.js +71 -2
- package/package.json +1 -1
|
@@ -33,7 +33,9 @@ function reactive(root, opts = {}) {
|
|
|
33
33
|
const { drain = 'immediate', depth = Infinity, eager = false } = opts;
|
|
34
34
|
const fire = scheduler(drain);
|
|
35
35
|
const eng = {
|
|
36
|
-
live: 0, pathLive: 0, dirty: new Set(), dirtyPaths: [],
|
|
36
|
+
live: 0, pathLive: 0, dirty: new Set(), dirtyPaths: [],
|
|
37
|
+
dirtyPathKeys: new Set(), pathKey: createPathKeyer(),
|
|
38
|
+
scheduled: false, waiters: new Set(), depth,
|
|
37
39
|
schedule() {
|
|
38
40
|
if (eng.scheduled)
|
|
39
41
|
return;
|
|
@@ -44,6 +46,8 @@ function reactive(root, opts = {}) {
|
|
|
44
46
|
eng.dirty.clear();
|
|
45
47
|
const dirtyPaths = eng.dirtyPaths;
|
|
46
48
|
eng.dirtyPaths = [];
|
|
49
|
+
eng.dirtyPathKeys = new Set();
|
|
50
|
+
eng.pathKey = createPathKeyer();
|
|
47
51
|
let err;
|
|
48
52
|
for (const n of batch) {
|
|
49
53
|
for (const cb of [...n.subs]) {
|
|
@@ -112,7 +116,6 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
112
116
|
v = toRaw(v);
|
|
113
117
|
if (Object.is(node.target[k], v))
|
|
114
118
|
return true;
|
|
115
|
-
const dirtyPath = dirtyPathFor(node, k);
|
|
116
119
|
node.target[k] = v;
|
|
117
120
|
if (Array.isArray(proxyTarget) && k == "length")
|
|
118
121
|
proxyTarget.length = v;
|
|
@@ -120,7 +123,7 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
120
123
|
if (kid)
|
|
121
124
|
rebind(kid, v);
|
|
122
125
|
if (eng.live > 0)
|
|
123
|
-
bubble(node,
|
|
126
|
+
bubble(node, k);
|
|
124
127
|
return true;
|
|
125
128
|
},
|
|
126
129
|
defineProperty(_, k, d) {
|
|
@@ -136,7 +139,6 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
136
139
|
}
|
|
137
140
|
const v = node.target[k];
|
|
138
141
|
if (!Object.is(old, v)) {
|
|
139
|
-
const dirtyPath = dirtyPathFor(node, k);
|
|
140
142
|
const kid = node.kids.get(k);
|
|
141
143
|
if (kid) {
|
|
142
144
|
if (isReactiveObj(v))
|
|
@@ -148,14 +150,13 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
if (eng.live > 0)
|
|
151
|
-
bubble(node,
|
|
153
|
+
bubble(node, k);
|
|
152
154
|
}
|
|
153
155
|
return true;
|
|
154
156
|
},
|
|
155
157
|
deleteProperty(_, k) {
|
|
156
158
|
if (!(k in node.target))
|
|
157
159
|
return true;
|
|
158
|
-
const dirtyPath = dirtyPathFor(node, k);
|
|
159
160
|
delete node.target[k];
|
|
160
161
|
const kid = node.kids.get(k);
|
|
161
162
|
if (kid) {
|
|
@@ -164,7 +165,7 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
164
165
|
detachTree(kid);
|
|
165
166
|
}
|
|
166
167
|
if (eng.live > 0)
|
|
167
|
-
bubble(node,
|
|
168
|
+
bubble(node, k);
|
|
168
169
|
return true;
|
|
169
170
|
},
|
|
170
171
|
has(_, k) { return k in node.target; },
|
|
@@ -191,10 +192,10 @@ function makeNode(target, parent, path, level, eng) {
|
|
|
191
192
|
});
|
|
192
193
|
return node;
|
|
193
194
|
}
|
|
194
|
-
function bubble(from,
|
|
195
|
+
function bubble(from, key) {
|
|
195
196
|
const eng = from.eng;
|
|
196
|
-
if (eng.pathLive > 0
|
|
197
|
-
addDirtyPath(eng,
|
|
197
|
+
if (eng.pathLive > 0)
|
|
198
|
+
addDirtyPath(eng, dirtyPathFor(from, key));
|
|
198
199
|
for (let n = from; n && n.active; n = n.parent)
|
|
199
200
|
if (n.subs.size || n.pathSubs.size)
|
|
200
201
|
eng.dirty.add(n);
|
|
@@ -224,26 +225,55 @@ function markChanged(node) {
|
|
|
224
225
|
function dirtyPathFor(node, key) {
|
|
225
226
|
return Array.isArray(node.target) ? [...node.path] : [...node.path, key];
|
|
226
227
|
}
|
|
227
|
-
function
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
function createPathKeyer() {
|
|
229
|
+
let symIds = null;
|
|
230
|
+
return function pathKey(path) {
|
|
231
|
+
let out = '';
|
|
232
|
+
for (const p of path) {
|
|
233
|
+
if (typeof p == 'symbol') {
|
|
234
|
+
symIds ??= new Map();
|
|
235
|
+
let id = symIds.get(p);
|
|
236
|
+
if (id == null) {
|
|
237
|
+
id = symIds.size;
|
|
238
|
+
symIds.set(p, id);
|
|
239
|
+
}
|
|
240
|
+
out += 'y' + id + '|';
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
const s = String(p);
|
|
244
|
+
out += (typeof p)[0] + s.length + ':' + s + '|';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
};
|
|
230
249
|
}
|
|
231
|
-
function
|
|
232
|
-
|
|
250
|
+
function addDirtyPath(eng, path) {
|
|
251
|
+
const k = eng.pathKey(path);
|
|
252
|
+
if (eng.dirtyPathKeys.has(k))
|
|
253
|
+
return;
|
|
254
|
+
eng.dirtyPathKeys.add(k);
|
|
255
|
+
eng.dirtyPaths.push(path);
|
|
233
256
|
}
|
|
234
257
|
function startsWithPath(path, prefix) {
|
|
235
258
|
return prefix.length <= path.length && prefix.every((k, i) => Object.is(k, path[i]));
|
|
236
259
|
}
|
|
237
260
|
function pathsForNode(node, dirtyPaths) {
|
|
238
261
|
const out = [];
|
|
262
|
+
const seen = new Set();
|
|
263
|
+
const pathKey = createPathKeyer();
|
|
239
264
|
for (const path of dirtyPaths) {
|
|
240
265
|
let next = null;
|
|
241
266
|
if (startsWithPath(path, node.path))
|
|
242
267
|
next = path.slice(node.path.length);
|
|
243
268
|
else if (startsWithPath(node.path, path))
|
|
244
269
|
next = [];
|
|
245
|
-
if (next
|
|
246
|
-
|
|
270
|
+
if (next == null)
|
|
271
|
+
continue;
|
|
272
|
+
const k = pathKey(next);
|
|
273
|
+
if (seen.has(k))
|
|
274
|
+
continue;
|
|
275
|
+
seen.add(k);
|
|
276
|
+
out.push(next);
|
|
247
277
|
}
|
|
248
278
|
return out;
|
|
249
279
|
}
|
|
@@ -6,31 +6,7 @@ export type StoreReplayOpts = Pick<ReplayListenOptions<[StorePatch]>, 'history'
|
|
|
6
6
|
export declare function storePatchKey(patch: StorePatch): string | null;
|
|
7
7
|
export declare function exposeStoreReplay<T extends object>(store: Store<T>, opts?: StoreReplayOpts): {
|
|
8
8
|
api: {
|
|
9
|
-
replay:
|
|
10
|
-
line: {
|
|
11
|
-
func: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>;
|
|
12
|
-
isRun: () => boolean;
|
|
13
|
-
run: () => void;
|
|
14
|
-
close: () => void;
|
|
15
|
-
eventClose: (cb: () => void) => () => void;
|
|
16
|
-
onClose: (cb: () => void) => () => void;
|
|
17
|
-
removeEventClose: (cb: () => void) => void;
|
|
18
|
-
on: import("../..").ListenOn<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>;
|
|
19
|
-
off: (k: (string | symbol) | import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]> | null) => void;
|
|
20
|
-
addListen: (cb: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>, opts?: {
|
|
21
|
-
cbClose?: () => void;
|
|
22
|
-
key?: string | symbol;
|
|
23
|
-
}) => () => void;
|
|
24
|
-
removeListen: (k: (string | symbol) | import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]> | null) => void;
|
|
25
|
-
once: (cb: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>, opts?: {
|
|
26
|
-
key?: string | symbol;
|
|
27
|
-
}) => () => void;
|
|
28
|
-
count: () => number;
|
|
29
|
-
readonly getAllKeys: (string | symbol)[];
|
|
30
|
-
};
|
|
31
|
-
since: (seq: number) => import("../events/replay-listen").ReplayEvent<[StorePatch]>[] | null;
|
|
32
|
-
keyframe: () => import("../events/replay-listen").ReplayEvent<[StorePatch]> | null;
|
|
33
|
-
};
|
|
9
|
+
replay: import("../events/replay-wire").ReplayExpose<[StorePatch]>;
|
|
34
10
|
get(): T;
|
|
35
11
|
get<M extends import("./store").StoreMask<T>>(mask: M): import("./store").StorePick<T, M>;
|
|
36
12
|
set(path: import("./store").StorePath, value: any): void;
|
|
@@ -43,6 +19,9 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
|
|
|
43
19
|
replay: {
|
|
44
20
|
func: import("../..").Listener<[StorePatch]>;
|
|
45
21
|
head: () => number;
|
|
22
|
+
isStale: () => boolean;
|
|
23
|
+
lastTs: () => number;
|
|
24
|
+
close: () => void;
|
|
46
25
|
getSince: (seq: number) => import("../events/replay-listen").ReplayEvent<[StorePatch]>[] | undefined;
|
|
47
26
|
line: {
|
|
48
27
|
func: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>;
|
|
@@ -83,7 +62,6 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
|
|
|
83
62
|
getAllKeys: (string | symbol)[];
|
|
84
63
|
isRun: () => boolean;
|
|
85
64
|
run: () => void;
|
|
86
|
-
close: () => void;
|
|
87
65
|
eventClose: (cb: () => void) => () => void;
|
|
88
66
|
onClose: (cb: () => void) => () => void;
|
|
89
67
|
removeEventClose: (cb: () => void) => void;
|
|
@@ -95,6 +73,8 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
|
|
|
95
73
|
export declare function syncStoreReplay<T extends object>(store: Store<T>, remote: ReplayRemote<[StorePatch]>, opts?: ReplaySubscribeOpts): (() => void) & {
|
|
96
74
|
ready: Promise<void>;
|
|
97
75
|
seq: () => number;
|
|
76
|
+
isStale: () => boolean;
|
|
77
|
+
lastTs: () => number;
|
|
98
78
|
};
|
|
99
79
|
export declare function storeReplayAt<T extends object>(storage: ReplayStorage<[StorePatch]>, at?: {
|
|
100
80
|
seq?: number;
|
|
@@ -122,6 +122,9 @@ function snapshotValue(value, seen = new WeakMap()) {
|
|
|
122
122
|
value = (0, reactive2_1.toRaw)(value);
|
|
123
123
|
if (!isObj(value))
|
|
124
124
|
return value;
|
|
125
|
+
const old = seen.get(value);
|
|
126
|
+
if (old)
|
|
127
|
+
return old;
|
|
125
128
|
if (value instanceof Date)
|
|
126
129
|
return new Date(value.valueOf());
|
|
127
130
|
if (value instanceof RegExp)
|
|
@@ -138,9 +141,6 @@ function snapshotValue(value, seen = new WeakMap()) {
|
|
|
138
141
|
value.forEach(v => out.add(snapshotValue(v, seen)));
|
|
139
142
|
return out;
|
|
140
143
|
}
|
|
141
|
-
const old = seen.get(value);
|
|
142
|
-
if (old)
|
|
143
|
-
return old;
|
|
144
144
|
const out = Array.isArray(value) ? [] : {};
|
|
145
145
|
seen.set(value, out);
|
|
146
146
|
for (const k of Reflect.ownKeys(value))
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.conflateReplay = conflateReplay;
|
|
4
4
|
const Listen3_1 = require("./Listen3");
|
|
5
|
-
const replay_wire_1 = require("./replay-wire");
|
|
6
5
|
function conflateReplay(replay, opts) {
|
|
7
6
|
const { pending, highWater, lowWater = 0, pollMs = 25, keyOf, maxKeys = 1024 } = opts;
|
|
8
7
|
if (!replay.hasKeyframe)
|
|
@@ -92,7 +91,11 @@ function conflateReplay(replay, opts) {
|
|
|
92
91
|
gate.close();
|
|
93
92
|
}
|
|
94
93
|
return {
|
|
95
|
-
api: {
|
|
94
|
+
api: {
|
|
95
|
+
line: gate,
|
|
96
|
+
since: (seq) => replay.getSince(seq) ?? null,
|
|
97
|
+
keyframe: () => replay.keyframe() ?? null,
|
|
98
|
+
},
|
|
96
99
|
close,
|
|
97
100
|
stats: () => ({ conflating, dropped, keyframes, coalesced, flushes }),
|
|
98
101
|
};
|
|
@@ -6,12 +6,19 @@ export type ReplayEvent<Z extends any[] = any[]> = {
|
|
|
6
6
|
ts: number;
|
|
7
7
|
event: Z;
|
|
8
8
|
};
|
|
9
|
+
export type StaleInfo = {
|
|
10
|
+
stale: boolean;
|
|
11
|
+
lastTs: number;
|
|
12
|
+
age: number;
|
|
13
|
+
};
|
|
9
14
|
export type ReplayListenOptions<Z extends any[]> = {
|
|
10
15
|
current?: ListenCurrentProvider<Z>;
|
|
11
16
|
history?: number;
|
|
12
17
|
getSince?: (seq: number) => ReplayEvent<Z>[] | undefined;
|
|
13
18
|
onJournal?: (ev: ReplayEvent<Z>) => void;
|
|
14
19
|
now?: () => number;
|
|
20
|
+
staleMs?: number;
|
|
21
|
+
onStale?: (info: StaleInfo) => void;
|
|
15
22
|
};
|
|
16
23
|
type ReplayOnOptions<Z extends any[]> = {
|
|
17
24
|
cbClose?: cbClose;
|
|
@@ -24,6 +31,9 @@ export type ListenOnReplay<Z extends any[] = any[]> = ((cb: Listener<Z>, opts?:
|
|
|
24
31
|
export declare function withReplayListen<T>(base: ListenApi<T>, options?: ReplayListenOptions<NormalizeTuple<T>>): {
|
|
25
32
|
func: Listener<NormalizeTuple<T>>;
|
|
26
33
|
head: () => number;
|
|
34
|
+
isStale: () => boolean;
|
|
35
|
+
lastTs: () => number;
|
|
36
|
+
close: () => void;
|
|
27
37
|
getSince: (seq: number) => ReplayEvent<NormalizeTuple<T>>[] | undefined;
|
|
28
38
|
line: {
|
|
29
39
|
func: Listener<[ReplayEvent<NormalizeTuple<T>>]>;
|
|
@@ -58,7 +68,6 @@ export declare function withReplayListen<T>(base: ListenApi<T>, options?: Replay
|
|
|
58
68
|
getAllKeys: key[];
|
|
59
69
|
isRun: () => boolean;
|
|
60
70
|
run: () => void;
|
|
61
|
-
close: () => void;
|
|
62
71
|
eventClose: (cb: () => void) => () => void;
|
|
63
72
|
onClose: (cb: () => void) => () => void;
|
|
64
73
|
removeEventClose: (cb: () => void) => void;
|
|
@@ -70,6 +79,9 @@ export type ReplayListenUseOptions<T> = ListenOptions<T> & ReplayListenOptions<N
|
|
|
70
79
|
export declare function UseReplayListen<T>(options?: ReplayListenUseOptions<T>): readonly [(...a: NormalizeTuple<T>) => void, {
|
|
71
80
|
func: Listener<NormalizeTuple<T>>;
|
|
72
81
|
head: () => number;
|
|
82
|
+
isStale: () => boolean;
|
|
83
|
+
lastTs: () => number;
|
|
84
|
+
close: () => void;
|
|
73
85
|
getSince: (seq: number) => ReplayEvent<NormalizeTuple<T>>[] | undefined;
|
|
74
86
|
line: {
|
|
75
87
|
func: Listener<[ReplayEvent<NormalizeTuple<T>>]>;
|
|
@@ -104,7 +116,6 @@ export declare function UseReplayListen<T>(options?: ReplayListenUseOptions<T>):
|
|
|
104
116
|
getAllKeys: key[];
|
|
105
117
|
isRun: () => boolean;
|
|
106
118
|
run: () => void;
|
|
107
|
-
close: () => void;
|
|
108
119
|
eventClose: (cb: () => void) => () => void;
|
|
109
120
|
onClose: (cb: () => void) => () => void;
|
|
110
121
|
removeEventClose: (cb: () => void) => void;
|
|
@@ -4,7 +4,7 @@ exports.withReplayListen = withReplayListen;
|
|
|
4
4
|
exports.UseReplayListen = UseReplayListen;
|
|
5
5
|
const Listen3_1 = require("./Listen3");
|
|
6
6
|
function withReplayListen(base, options = {}) {
|
|
7
|
-
const { current, history = 0, getSince, onJournal, now = Date.now } = options;
|
|
7
|
+
const { current, history = 0, getSince, onJournal, now = Date.now, staleMs, onStale } = options;
|
|
8
8
|
let head = 0;
|
|
9
9
|
const ring = [];
|
|
10
10
|
let emitting = null;
|
|
@@ -30,6 +30,44 @@ function withReplayListen(base, options = {}) {
|
|
|
30
30
|
return c();
|
|
31
31
|
return c ? current?.() : undefined;
|
|
32
32
|
}
|
|
33
|
+
let lastTs = 0;
|
|
34
|
+
let staleFlag = false;
|
|
35
|
+
let staleTimer = null;
|
|
36
|
+
function stopStaleTimer() {
|
|
37
|
+
if (staleTimer) {
|
|
38
|
+
clearTimeout(staleTimer);
|
|
39
|
+
staleTimer = null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function reportStale(stale) {
|
|
43
|
+
staleFlag = stale;
|
|
44
|
+
try {
|
|
45
|
+
onStale({ stale, lastTs, age: now() - lastTs });
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
setTimeout(function rethrowOnStale() { throw e; }, 0);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function checkStale() {
|
|
52
|
+
staleTimer = null;
|
|
53
|
+
const age = now() - lastTs;
|
|
54
|
+
if (age >= staleMs)
|
|
55
|
+
reportStale(true);
|
|
56
|
+
else
|
|
57
|
+
armStaleTimer(staleMs - age);
|
|
58
|
+
}
|
|
59
|
+
function armStaleTimer(delay) {
|
|
60
|
+
if (staleTimer || !onStale || staleMs == null)
|
|
61
|
+
return;
|
|
62
|
+
staleTimer = setTimeout(checkStale, delay);
|
|
63
|
+
staleTimer.unref?.();
|
|
64
|
+
}
|
|
65
|
+
function touchStale(ts) {
|
|
66
|
+
lastTs = ts;
|
|
67
|
+
if (staleFlag)
|
|
68
|
+
reportStale(false);
|
|
69
|
+
armStaleTimer(staleMs);
|
|
70
|
+
}
|
|
33
71
|
const api = {
|
|
34
72
|
...base,
|
|
35
73
|
func: function emitJournaled(...e) {
|
|
@@ -37,6 +75,7 @@ function withReplayListen(base, options = {}) {
|
|
|
37
75
|
if (history > 0)
|
|
38
76
|
ring[(ev.seq - 1) % history] = ev;
|
|
39
77
|
onJournal?.(ev);
|
|
78
|
+
touchStale(ev.ts);
|
|
40
79
|
line.func(ev);
|
|
41
80
|
const prev = emitting;
|
|
42
81
|
emitting = ev;
|
|
@@ -48,6 +87,9 @@ function withReplayListen(base, options = {}) {
|
|
|
48
87
|
}
|
|
49
88
|
},
|
|
50
89
|
head: () => head,
|
|
90
|
+
isStale: () => staleMs != null && head > 0 && now() - lastTs >= staleMs,
|
|
91
|
+
lastTs: () => lastTs,
|
|
92
|
+
close: function closeReplay() { stopStaleTimer(); base.close(); },
|
|
51
93
|
getSince: journalSince,
|
|
52
94
|
line,
|
|
53
95
|
hasKeyframe: current != null,
|
|
@@ -124,10 +166,10 @@ function withReplayListen(base, options = {}) {
|
|
|
124
166
|
return api;
|
|
125
167
|
}
|
|
126
168
|
function UseReplayListen(options = {}) {
|
|
127
|
-
const { current, history, getSince, onJournal, now, ...listenOptions } = options;
|
|
169
|
+
const { current, history, getSince, onJournal, now, staleMs, onStale, ...listenOptions } = options;
|
|
128
170
|
let t;
|
|
129
171
|
const base = (0, Listen3_1.funcListenCallbackBase)((e) => { t = e; }, { fast: true, ...listenOptions });
|
|
130
|
-
const listen = withReplayListen(base, { current, history, getSince, onJournal, now });
|
|
172
|
+
const listen = withReplayListen(base, { current, history, getSince, onJournal, now, staleMs, onStale });
|
|
131
173
|
base.run();
|
|
132
174
|
t = listen.func;
|
|
133
175
|
return [t, listen];
|
|
@@ -1,29 +1,17 @@
|
|
|
1
|
-
import { Listener } from './Listen3';
|
|
2
|
-
import { ListenReplayApi, ReplayEvent } from './replay-listen';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
cbClose?: () => void;
|
|
16
|
-
key?: string | symbol;
|
|
17
|
-
}) => () => void;
|
|
18
|
-
removeListen: (k: (string | symbol) | Listener<[ReplayEvent<import("./Listen3").NormalizeTuple<T>>]> | null) => void;
|
|
19
|
-
once: (cb: Listener<[ReplayEvent<import("./Listen3").NormalizeTuple<T>>]>, opts?: {
|
|
20
|
-
key?: string | symbol;
|
|
21
|
-
}) => () => void;
|
|
22
|
-
count: () => number;
|
|
23
|
-
readonly getAllKeys: (string | symbol)[];
|
|
24
|
-
};
|
|
25
|
-
since: (seq: number) => ReplayEvent<import("./Listen3").NormalizeTuple<T>>[] | null;
|
|
26
|
-
keyframe: () => ReplayEvent<import("./Listen3").NormalizeTuple<T>> | null;
|
|
1
|
+
import { Listener, NormalizeTuple } from './Listen3';
|
|
2
|
+
import { ListenReplayApi, ReplayEvent, StaleInfo } from './replay-listen';
|
|
3
|
+
import { conflateReplay, ConflateOpts } from './replay-conflate';
|
|
4
|
+
export type ReplayExpose<T> = {
|
|
5
|
+
line: ListenReplayApi<T>['line'];
|
|
6
|
+
since: (seq: number) => ReplayEvent<NormalizeTuple<T>>[] | null;
|
|
7
|
+
keyframe: () => ReplayEvent<NormalizeTuple<T>> | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function exposeReplay<T>(replay: ListenReplayApi<T>): ReplayExpose<T>;
|
|
10
|
+
export declare function exposeReplay<T>(replay: ListenReplayApi<T>, opts: {
|
|
11
|
+
conflate: ConflateOpts<NormalizeTuple<T>>;
|
|
12
|
+
}): ReplayExpose<T> & {
|
|
13
|
+
close: () => void;
|
|
14
|
+
stats: ReturnType<typeof conflateReplay<T>>['stats'];
|
|
27
15
|
};
|
|
28
16
|
export type ReplayRemote<Z extends any[] = any[]> = {
|
|
29
17
|
line: {
|
|
@@ -36,8 +24,14 @@ export type ReplaySubscribeOpts = {
|
|
|
36
24
|
since?: number;
|
|
37
25
|
onSeq?: (seq: number) => void;
|
|
38
26
|
onError?: (e: any) => void;
|
|
27
|
+
staleMs?: number;
|
|
28
|
+
onStale?: (info: StaleInfo) => void;
|
|
29
|
+
skewMs?: number;
|
|
30
|
+
now?: () => number;
|
|
39
31
|
};
|
|
40
32
|
export declare function replaySubscribe<Z extends any[]>(remote: ReplayRemote<Z>, cb: Listener<Z>, opts?: ReplaySubscribeOpts): (() => void) & {
|
|
41
33
|
ready: Promise<void>;
|
|
42
34
|
seq: () => number;
|
|
35
|
+
isStale: () => boolean;
|
|
36
|
+
lastTs: () => number;
|
|
43
37
|
};
|
|
@@ -2,13 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.exposeReplay = exposeReplay;
|
|
4
4
|
exports.replaySubscribe = replaySubscribe;
|
|
5
|
-
|
|
5
|
+
const replay_conflate_1 = require("./replay-conflate");
|
|
6
|
+
function exposeReplayPlain(replay) {
|
|
6
7
|
return {
|
|
7
8
|
line: replay.line,
|
|
8
9
|
since: (seq) => replay.getSince(seq) ?? null,
|
|
9
10
|
keyframe: () => replay.keyframe() ?? null,
|
|
10
11
|
};
|
|
11
12
|
}
|
|
13
|
+
function exposeReplay(replay, opts) {
|
|
14
|
+
if (!opts?.conflate)
|
|
15
|
+
return exposeReplayPlain(replay);
|
|
16
|
+
const gated = (0, replay_conflate_1.conflateReplay)(replay, opts.conflate);
|
|
17
|
+
return { ...gated.api, close: gated.close, stats: gated.stats };
|
|
18
|
+
}
|
|
12
19
|
function unsubscribeHandle(handle) {
|
|
13
20
|
if (typeof handle == 'function') {
|
|
14
21
|
handle();
|
|
@@ -20,19 +27,75 @@ function unsubscribeHandle(handle) {
|
|
|
20
27
|
handle.unsubscribe();
|
|
21
28
|
}
|
|
22
29
|
function replaySubscribe(remote, cb, opts = {}) {
|
|
23
|
-
const { since = -1, onSeq, onError } = opts;
|
|
30
|
+
const { since = -1, onSeq, onError, staleMs, onStale, skewMs = 0, now = Date.now } = opts;
|
|
24
31
|
let lastDelivered = since;
|
|
25
32
|
let replaying = true;
|
|
26
33
|
let closed = false;
|
|
27
34
|
const queue = [];
|
|
35
|
+
let lastTs = 0;
|
|
36
|
+
let lastArrival = now();
|
|
37
|
+
let staleFlag = false;
|
|
38
|
+
let staleTimer = null;
|
|
39
|
+
function stopStaleTimer() {
|
|
40
|
+
if (staleTimer) {
|
|
41
|
+
clearTimeout(staleTimer);
|
|
42
|
+
staleTimer = null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function reportStale(stale) {
|
|
46
|
+
staleFlag = stale;
|
|
47
|
+
if (!onStale)
|
|
48
|
+
return;
|
|
49
|
+
try {
|
|
50
|
+
onStale({ stale, lastTs, age: now() - (lastTs || lastArrival) });
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
setTimeout(function rethrowOnStale() { throw e; }, 0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function checkArrivalGap() {
|
|
57
|
+
staleTimer = null;
|
|
58
|
+
if (closed)
|
|
59
|
+
return;
|
|
60
|
+
const gap = now() - lastArrival;
|
|
61
|
+
if (gap >= staleMs) {
|
|
62
|
+
if (!staleFlag)
|
|
63
|
+
reportStale(true);
|
|
64
|
+
}
|
|
65
|
+
else
|
|
66
|
+
armStaleTimer(staleMs - gap);
|
|
67
|
+
}
|
|
68
|
+
function armStaleTimer(delay) {
|
|
69
|
+
if (staleTimer || !onStale || staleMs == null || closed)
|
|
70
|
+
return;
|
|
71
|
+
staleTimer = setTimeout(checkArrivalGap, delay);
|
|
72
|
+
staleTimer.unref?.();
|
|
73
|
+
}
|
|
74
|
+
function assessStale() {
|
|
75
|
+
if (staleMs == null || closed)
|
|
76
|
+
return;
|
|
77
|
+
const tsStale = lastTs > 0 && now() - lastTs > staleMs + skewMs;
|
|
78
|
+
if (tsStale != staleFlag)
|
|
79
|
+
reportStale(tsStale);
|
|
80
|
+
if (tsStale)
|
|
81
|
+
stopStaleTimer();
|
|
82
|
+
else
|
|
83
|
+
armStaleTimer(staleMs);
|
|
84
|
+
}
|
|
85
|
+
armStaleTimer(staleMs);
|
|
28
86
|
function deliver(ev) {
|
|
29
87
|
if (closed || ev.seq <= lastDelivered)
|
|
30
88
|
return;
|
|
31
89
|
lastDelivered = ev.seq;
|
|
90
|
+
lastTs = ev.ts;
|
|
91
|
+
lastArrival = now();
|
|
32
92
|
cb(...ev.event);
|
|
33
93
|
onSeq?.(ev.seq);
|
|
94
|
+
if (!replaying)
|
|
95
|
+
assessStale();
|
|
34
96
|
}
|
|
35
97
|
const handle = remote.line.on(function liveTap(ev) {
|
|
98
|
+
lastArrival = now();
|
|
36
99
|
if (replaying)
|
|
37
100
|
queue.push(ev);
|
|
38
101
|
else
|
|
@@ -53,6 +116,8 @@ function replaySubscribe(remote, cb, opts = {}) {
|
|
|
53
116
|
return;
|
|
54
117
|
if (kf) {
|
|
55
118
|
lastDelivered = kf.seq;
|
|
119
|
+
lastTs = kf.ts;
|
|
120
|
+
lastArrival = now();
|
|
56
121
|
cb(...kf.event);
|
|
57
122
|
onSeq?.(kf.seq);
|
|
58
123
|
}
|
|
@@ -68,6 +133,7 @@ function replaySubscribe(remote, cb, opts = {}) {
|
|
|
68
133
|
while (queue.length)
|
|
69
134
|
deliver(queue.shift());
|
|
70
135
|
replaying = false;
|
|
136
|
+
assessStale();
|
|
71
137
|
}
|
|
72
138
|
}
|
|
73
139
|
const ready = catchUp();
|
|
@@ -75,10 +141,13 @@ function replaySubscribe(remote, cb, opts = {}) {
|
|
|
75
141
|
if (closed)
|
|
76
142
|
return;
|
|
77
143
|
closed = true;
|
|
144
|
+
stopStaleTimer();
|
|
78
145
|
unsubscribeHandle(handle);
|
|
79
146
|
}
|
|
80
147
|
return Object.assign(off, {
|
|
81
148
|
ready,
|
|
82
149
|
seq: () => lastDelivered,
|
|
150
|
+
isStale: () => staleFlag || (staleMs != null && now() - lastArrival >= staleMs),
|
|
151
|
+
lastTs: () => lastTs,
|
|
83
152
|
});
|
|
84
153
|
}
|