wenay-common2 1.0.57 → 1.0.58

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.
@@ -1,5 +1,5 @@
1
1
  import { Store, StorePatch } from './store';
2
- import { ReplayListenOptions } from '../events/replay-listen';
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) => import("../events/replay-listen").ReplayEvent<[StorePatch]>[] | undefined;
25
+ getSince: (seq: number) => ReplayEvent<[StorePatch]>[] | undefined;
26
26
  line: {
27
- func: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>;
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<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>;
35
- off: (k: (string | symbol) | import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]> | null) => void;
36
- addListen: (cb: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>, opts?: {
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<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]> | null) => void;
41
- once: (cb: import("../..").Listener<[import("../events/replay-listen").ReplayEvent<[StorePatch]>]>, opts?: {
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: () => import("../events/replay-listen").ReplayEvent<[StorePatch]> | undefined;
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]> | (string | symbol) | null) => void;
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;
@@ -21,9 +21,21 @@ function storePatchKey(patch) {
21
21
  return null;
22
22
  return JSON.stringify(patch.path);
23
23
  }
24
+ function condensePatchTail(tail) {
25
+ const held = new Map();
26
+ for (const ev of tail) {
27
+ const k = storePatchKey(ev.event[0]);
28
+ if (k == null)
29
+ return tail;
30
+ held.delete(k);
31
+ held.set(k, ev);
32
+ }
33
+ return [...held.values()];
34
+ }
24
35
  function exposeStoreReplay(store, opts = {}) {
25
36
  const [emitPatch, lineApi] = (0, replay_listen_1.UseReplayListen)({
26
37
  current: () => [{ path: [], exists: true, value: store.snapshot() }],
38
+ frame: condensePatchTail,
27
39
  history: opts.getSince ? undefined : (opts.history ?? 1024),
28
40
  getSince: opts.getSince,
29
41
  onJournal: opts.onJournal,
@@ -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 handle = remote.line.on(function liveTap(ev) {
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
- const tail = since >= 0 ? await remote.since(since) : null;
107
- if (closed)
108
- return;
109
- if (tail) {
110
- for (const ev of tail)
111
- deliver(ev);
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
- else {
114
- const kf = await remote.keyframe();
133
+ if (!done) {
134
+ const tail = since >= 0 ? await remote.since(since) : null;
115
135
  if (closed)
116
136
  return;
117
- if (kf) {
118
- lastDelivered = kf.seq;
119
- lastTs = kf.ts;
120
- lastArrival = now();
121
- cb(...kf.event);
122
- onSeq?.(kf.seq);
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;
@@ -11,8 +12,21 @@ type Obj = Record<string, any>;
11
12
  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
+ 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
  };
@@ -3,7 +3,9 @@ import { type RpcLimits } from "./rpc-limits";
3
3
  import { makeOff } from "./rpc-off";
4
4
  import { type RpcOpt } from "./rpc-caps";
5
5
  type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
6
- export type DeepDataOnly<T> = T extends Function ? never : T extends Array<infer U> ? Array<DeepDataOnly<U>> : T extends object ? {
6
+ export type DeepDataOnly<T> = T extends Function ? never : T extends readonly any[] ? {
7
+ [I in keyof T]: DeepDataOnly<T[I]>;
8
+ } : T extends object ? {
7
9
  [K in keyof T as T[K] extends Function ? never : K]: DeepDataOnly<T[K]>;
8
10
  } : T;
9
11
  export type ClientAPIAll<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 declare function createRpcServerAuto<T extends object>({ socket, object: target, socketKey: key, debug, hooks, disconnectListen, limits, auth, maxPerListen, throttle, opt }: {
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
- return getListenSocket((0, Listen_1.getListenByOn)(obj), disconnectListen);
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
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wenay-common2",
3
- "version": "1.0.57",
3
+ "version": "1.0.58",
4
4
  "description": "Common library",
5
5
  "strict": true,
6
6
  "main": "lib/index.js",