wenay-common2 1.0.58 → 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.
@@ -1,4 +1,4 @@
1
- import { Store, StorePatch } from './store';
1
+ import { Store, StorePatch, StoreDrain, StoreEachCtx } from './store';
2
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';
@@ -77,6 +77,16 @@ export declare function syncStoreReplay<T extends object>(store: Store<T>, remot
77
77
  isStale: () => boolean;
78
78
  lastTs: () => number;
79
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
+ };
80
90
  export declare function storeReplayAt<T extends object>(storage: ReplayStorage<[StorePatch]>, at?: {
81
91
  seq?: number;
82
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");
@@ -54,6 +55,20 @@ function exposeStoreReplay(store, opts = {}) {
54
55
  function syncStoreReplay(store, remote, opts = {}) {
55
56
  return (0, replay_wire_1.replaySubscribe)(remote, function applyLine(patch) { (0, store_1.applyStorePatch)(store, patch); }, opts);
56
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
+ }
57
72
  function storeReplayAt(storage, at = {}) {
58
73
  const envelopes = (0, replay_history_1.openHistory)(storage).at(at);
59
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),
@@ -9,7 +9,7 @@ type WithSubHandle<R> = R extends {
9
9
  once: (...a: A) => tSubHandle & Promise<void>;
10
10
  } : R;
11
11
  type Obj = Record<string, any>;
12
- type InferArgs<T> = T extends {
12
+ export type InferArgs<T> = T extends {
13
13
  addListen: (cb: (...args: infer R) => void, ...rest: any[]) => any;
14
14
  } ? R : never;
15
15
  export type ReplaySocketListen<Z extends any[]> = WithSubHandle<ReturnType<typeof listenSocket<Z>>> & {
@@ -19,7 +19,7 @@ export type ReplaySocketListen<Z extends any[]> = WithSubHandle<ReturnType<typeo
19
19
  keyframe: () => Promise<ReplayEvent<Z> | null>;
20
20
  frame: (seq: number, hint?: unknown) => Promise<ReplayEvent<Z>[]>;
21
21
  };
22
- type IsReplayMember<V> = V extends {
22
+ export type IsReplayMember<V> = V extends {
23
23
  addListen: Function;
24
24
  getSince: Function;
25
25
  keyframe: Function;
@@ -2,6 +2,7 @@ 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
7
  export type DeepDataOnly<T> = T extends Function ? never : T extends readonly any[] ? {
7
8
  [I in keyof T]: DeepDataOnly<T[I]>;
@@ -9,11 +10,11 @@ export type DeepDataOnly<T> = T extends Function ? never : T extends readonly an
9
10
  [K in keyof T as T[K] extends Function ? never : K]: DeepDataOnly<T[K]>;
10
11
  } : T;
11
12
  export type ClientAPIAll<T> = {
12
- [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;
13
14
  };
14
15
  type NonFalsy<T> = Exclude<T, false | null | 0 | "" | undefined>;
15
16
  export type ClientAPIStrict<T> = {
16
- [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;
17
18
  };
18
19
  export interface PipeArrayAPI<T> extends Promise<DeepDataOnly<T[]>> {
19
20
  [index: number]: PipeAPI<T>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wenay-common2",
3
- "version": "1.0.58",
3
+ "version": "1.0.61",
4
4
  "description": "Common library",
5
5
  "strict": true,
6
6
  "main": "lib/index.js",