wenay-common2 1.0.61 → 1.0.62

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.
Files changed (57) hide show
  1. package/README.md +8 -8
  2. package/lib/Common/{ObserveAll2 → Observe}/index.d.ts +1 -0
  3. package/lib/Common/{ObserveAll2 → Observe}/index.js +1 -0
  4. package/lib/Common/Observe/reactive2.d.ts +21 -0
  5. package/lib/Common/{ObserveAll2 → Observe}/reactive2.js +6 -6
  6. package/lib/Common/Observe/store-offline.d.ts +88 -0
  7. package/lib/Common/Observe/store-offline.js +278 -0
  8. package/lib/Common/{ObserveAll2 → Observe}/store-replay.d.ts +9 -38
  9. package/lib/Common/{ObserveAll2 → Observe}/store-replay.js +1 -1
  10. package/lib/Common/{ObserveAll2 → Observe}/store.d.ts +2 -2
  11. package/lib/Common/{ObserveAll2 → Observe}/store.js +12 -18
  12. package/lib/Common/async/promiseProgress.d.ts +12 -0
  13. package/lib/Common/async/promiseProgress.js +29 -0
  14. package/lib/Common/events/Listen3.d.ts +81 -162
  15. package/lib/Common/events/Listen3.js +95 -105
  16. package/lib/Common/events/SocketBuffer.d.ts +13 -33
  17. package/lib/Common/events/SocketBuffer.js +8 -8
  18. package/lib/Common/events/SocketServerHook.d.ts +5 -45
  19. package/lib/Common/events/SocketServerHook.js +1 -1
  20. package/lib/Common/events/joinListens.d.ts +6 -7
  21. package/lib/Common/events/joinListens.js +3 -4
  22. package/lib/Common/events/mapListen.d.ts +4 -0
  23. package/lib/Common/events/mapListen.js +25 -0
  24. package/lib/Common/events/replay-conflate.d.ts +1 -21
  25. package/lib/Common/events/replay-conflate.js +4 -4
  26. package/lib/Common/events/replay-listen.d.ts +19 -65
  27. package/lib/Common/events/replay-listen.js +8 -11
  28. package/lib/Common/rcp/createRpcServerAutoWithProtocolDetection.d.ts +3 -3
  29. package/lib/Common/rcp/createRpcServerAutoWithProtocolDetection.js +10 -10
  30. package/lib/Common/rcp/listen-deep.d.ts +10 -10
  31. package/lib/Common/rcp/listen-deep.js +1 -1
  32. package/lib/Common/rcp/listen-socket.d.ts +13 -13
  33. package/lib/Common/rcp/listen-socket.js +4 -3
  34. package/lib/Common/rcp/rpc-client-auto.d.ts +2 -2
  35. package/lib/Common/rcp/rpc-client-auto.js +3 -2
  36. package/lib/Common/rcp/rpc-client.js +6 -4
  37. package/lib/Common/rcp/rpc-index.d.ts +2 -1
  38. package/lib/Common/rcp/rpc-index.js +3 -2
  39. package/lib/Common/rcp/rpc-path.d.ts +1 -0
  40. package/lib/Common/rcp/rpc-path.js +6 -0
  41. package/lib/Common/rcp/rpc-server-auto.d.ts +2 -2
  42. package/lib/Common/rcp/rpc-server-auto.js +7 -11
  43. package/lib/Common/rcp/rpc-server.js +11 -7
  44. package/lib/client.d.ts +1 -1
  45. package/lib/client.js +1 -1
  46. package/lib/index.d.ts +3 -4
  47. package/lib/index.js +4 -5
  48. package/package.json +2 -3
  49. package/lib/Common/ObserveAll2/reactive2.d.ts +0 -61
  50. package/lib/Common/async/PromiseArrayListen.d.ts +0 -14
  51. package/lib/Common/async/PromiseArrayListen.js +0 -37
  52. package/lib/Common/events/Listen2.d.ts +0 -1
  53. package/lib/Common/events/Listen2.js +0 -17
  54. package/lib/Common/events/UseListenTransform.d.ts +0 -24
  55. package/lib/Common/events/UseListenTransform.js +0 -28
  56. package/lib/Common/rcp/test.d.ts +0 -1
  57. package/lib/Common/rcp/test.js +0 -79
package/README.md CHANGED
@@ -18,13 +18,13 @@ A set of common utilities and components for TypeScript/Node.js projects.
18
18
 
19
19
  ### ⏱️ **async/** — Async Utilities
20
20
  - **`waitRun.ts`** — throttle/debounce (`enhancedWaitRun`), async queues (`createAsyncQueue`, `queueRun`), task queue with readiness control (`createTaskQueue`)
21
- - **`PromiseArrayListen.ts`** — processing an array of promises with success/error subscriptions
21
+ - **`promiseProgress.ts`** — processing an array of promises with success/error subscriptions
22
22
  - **`createIterableObject.ts`** — proxy for iterable objects (readonly/read-write)
23
23
 
24
24
  ### 🔔 **events/** — Events and Subscriptions
25
- - **`Listen.ts`** — subscription system (`UseListen`, `funcListenCallback`), `isListenCallback` check
25
+ - **`Listen.ts`** — subscription system (`listen`, `createListen`), `isListenCallback` check
26
26
  - **`event.ts`** — event handler collections (`CObjectEventsArr`, `CObjectEventsList`)
27
- - **`SocketBuffer.ts`** — socket callback buffering (`socketBuffer3`, `funcListenCallbackSnapshot`)
27
+ - **`SocketBuffer.ts`** — socket callback buffering (`socketBuffer`, `listenSnapshot`)
28
28
  - **`SocketServerHook.ts`** — tag-based subscription wrappers for socket communication (`SocketServerHook`, `WebSocketServerHook`)
29
29
  - **`joinListens.ts`** — merging multiple subscription streams into one (`joinListens`)
30
30
  - **`listen-socket.ts`** — bridge between event system and RPC (`listenSocket`, `listenSocketFirst`, `listenSocketAll`, `listenSocketSmart`)
@@ -92,11 +92,11 @@ Bidirectional, strongly-typed RPC protocol over sockets (Socket.IO or similar).
92
92
 
93
93
  ### 2.1 Socket Connection
94
94
  ```typescript
95
- import { createRpcServerAuto, UseListen } from "wenay-common2";
95
+ import { createRpcServerAuto, listen } from "wenay-common2";
96
96
 
97
97
  io.sockets.on('connection', (socket) => {
98
98
  // 1. Create unsubscribe trigger for memory cleanup
99
- const [stop, listenStop] = UseListen<[]>();
99
+ const [stop, listenStop] = listen<[]>();
100
100
  socket.on('disconnect', stop);
101
101
 
102
102
  // 2. Initialize RPC channel on this socket
@@ -113,10 +113,10 @@ io.sockets.on('connection', (socket) => {
113
113
  ### 2.2 Building API Object (Facade)
114
114
  The object is traversed by the server to build a "Schema" that is sent to the client.
115
115
  ```typescript
116
- import { noStrict, UseListen } from "wenay-common2";
116
+ import { noStrict, listen } from "wenay-common2";
117
117
 
118
118
  // Create pub/sub event system
119
- const [sendEvent, listenEvent] = UseListen<[string]>();
119
+ const [sendEvent, listenEvent] = listen<[string]>();
120
120
 
121
121
  export function buildFacade(client) {
122
122
  const role = (...roles) => hasRole(client, roles) ? true : null;
@@ -233,7 +233,7 @@ api.space.admin.logAction("clicked");
233
233
  ```
234
234
 
235
235
  ### 3.4 Client Subscriptions (Listen)
236
- `createRpcServerAuto` exposes server `UseListen` values as RPC Listen nodes. New code uses `on`/`once` and keeps the returned `off` handle. For TypeScript, project `client.func` to `DeepSocketListen<ServerFacade>`; this mirrors the runtime shape and keeps event argument types.
236
+ `createRpcServerAuto` exposes server `listen` values as RPC Listen nodes. New code uses `on`/`once` and keeps the returned `off` handle. For TypeScript, project `client.func` to `DeepSocketListen<ServerFacade>`; this mirrors the runtime shape and keeps event argument types.
237
237
  ```typescript
238
238
  import type { DeepSocketListen } from "wenay-common2";
239
239
 
@@ -1,3 +1,4 @@
1
1
  export * from "./reactive2";
2
2
  export * from "./store";
3
3
  export * from "./store-replay";
4
+ export * from "./store-offline";
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./reactive2"), exports);
18
18
  __exportStar(require("./store"), exports);
19
19
  __exportStar(require("./store-replay"), exports);
20
+ __exportStar(require("./store-offline"), exports);
@@ -0,0 +1,21 @@
1
+ type Fn = () => void;
2
+ export type ReactiveChange = {
3
+ paths: PropertyKey[][];
4
+ };
5
+ type PathUpdateFn = (change: ReactiveChange) => void;
6
+ type Drain = 'micro' | 'immediate' | number | ((flush: Fn) => void);
7
+ export type Opts = {
8
+ drain?: Drain;
9
+ depth?: number;
10
+ eager?: boolean;
11
+ };
12
+ export declare function reactive<T extends object>(root: T, opts?: Opts): T;
13
+ export declare function isReactive(p: any): boolean;
14
+ export declare function toRaw<T>(p: T): T;
15
+ export declare function onUpdate(p: any, cb: Fn): () => void;
16
+ export declare function onUpdatePaths(p: any, cb: PathUpdateFn): () => void;
17
+ export declare function flushReactive(p: any): Promise<void>;
18
+ export declare function listenUpdate(p: any): import("../events/Listen3").ListenApi<[]>;
19
+ export declare function listenUpdatePaths(p: any): import("../events/Listen3").ListenApi<[ReactiveChange]>;
20
+ export type Reactive<T extends object> = T;
21
+ export {};
@@ -351,22 +351,22 @@ function flushReactive(p) {
351
351
  return new Promise(resolve => { eng.waiters.add(resolve); });
352
352
  }
353
353
  function listenUpdate(p) {
354
- const listen = (0, Listen_1.funcListenCallbackBase)((emit) => onUpdate(p, () => emit()), {
354
+ const listen = (0, Listen_1.createListen)((emit) => onUpdate(p, () => emit()), {
355
355
  event: (type, count, api) => {
356
- if (type == "add" && count == 1 && !api.isRun())
356
+ if (type == "add" && count == 1 && !api.isRunning())
357
357
  api.run();
358
- if (type == "remove" && count == 0 && api.isRun())
358
+ if (type == "remove" && count == 0 && api.isRunning())
359
359
  api.close();
360
360
  },
361
361
  });
362
362
  return listen;
363
363
  }
364
364
  function listenUpdatePaths(p) {
365
- const listen = (0, Listen_1.funcListenCallbackBase)((emit) => onUpdatePaths(p, change => emit(change)), {
365
+ const listen = (0, Listen_1.createListen)((emit) => onUpdatePaths(p, change => emit(change)), {
366
366
  event: (type, count, api) => {
367
- if (type == "add" && count == 1 && !api.isRun())
367
+ if (type == "add" && count == 1 && !api.isRunning())
368
368
  api.run();
369
- if (type == "remove" && count == 0 && api.isRun())
369
+ if (type == "remove" && count == 0 && api.isRunning())
370
370
  api.close();
371
371
  },
372
372
  });
@@ -0,0 +1,88 @@
1
+ import { ReplayRemote, ReplaySubscribeOpts } from '../events/replay-wire';
2
+ import { createStore, Store, StorePatch } from './store';
3
+ export type OfflineStorage = {
4
+ read<T>(key: string): Promise<T | undefined>;
5
+ write<T>(key: string, value: T): Promise<void>;
6
+ remove(key: string): Promise<void>;
7
+ transaction?<R>(fn: (tx: OfflineStorage) => Promise<R>): Promise<R>;
8
+ };
9
+ export type OfflineStoreRecord<T extends object> = {
10
+ version: number;
11
+ seq: number;
12
+ snapshot: T;
13
+ savedAt: number;
14
+ };
15
+ export type OfflineStoreStatus = {
16
+ ready: boolean;
17
+ syncing: boolean;
18
+ offline: boolean;
19
+ stale: boolean;
20
+ saving: boolean;
21
+ seq: number;
22
+ savedAt?: number;
23
+ error?: unknown;
24
+ };
25
+ export type PersistStoreOpts = {
26
+ key: string;
27
+ storage: OfflineStorage;
28
+ version?: number;
29
+ seq?: number;
30
+ savedAt?: number;
31
+ debounceMs?: number;
32
+ now?: () => number;
33
+ onError?: (error: unknown) => void;
34
+ onStatus?: (status: OfflineStoreStatus) => void;
35
+ };
36
+ export type CreateOfflineStoreOpts<T extends object> = {
37
+ key: string;
38
+ remote?: ReplayRemote<[StorePatch]>;
39
+ initial: T;
40
+ storage: OfflineStorage;
41
+ version?: number;
42
+ debounceMs?: number;
43
+ mode?: 'snapshot' | 'topLevel';
44
+ migrate?: (oldSnapshot: unknown, fromVersion: number, toVersion: number) => T | Promise<T>;
45
+ now?: () => number;
46
+ storeOpts?: Parameters<typeof createStore<T>>[1];
47
+ syncOpts?: ReplaySubscribeOpts;
48
+ onError?: (error: unknown) => void;
49
+ onStatus?: (status: OfflineStoreStatus) => void;
50
+ };
51
+ export type PersistedStoreControl = ReturnType<typeof persistStore>;
52
+ export type OfflineStore<T extends object> = Store<T> & {
53
+ ready: Promise<void>;
54
+ close(): void;
55
+ flush(): Promise<void>;
56
+ status(): OfflineStoreStatus;
57
+ statusListen: PersistedStoreControl['statusListen'];
58
+ reconnect(remote: ReplayRemote<[StorePatch]>, opts?: ReplaySubscribeOpts): Promise<void>;
59
+ };
60
+ export declare function createMemoryOfflineStorage(initial?: Record<string, unknown>): OfflineStorage & {
61
+ dump(): Record<string, unknown>;
62
+ };
63
+ export declare function persistStore<T extends object>(store: Store<T>, opts: PersistStoreOpts): {
64
+ flush: () => Promise<void>;
65
+ forceFlush: () => Promise<void>;
66
+ close: () => void;
67
+ setSeq: (nextSeq: number) => void;
68
+ seq: () => number;
69
+ status: () => OfflineStoreStatus;
70
+ statusListen: {
71
+ on: import("../events/Listen3").ListenOnCurrent<[OfflineStoreStatus]>;
72
+ once: (cb: import("../events/Listen3").Listener<[OfflineStoreStatus]>, opts?: {
73
+ key?: import("../events/Listen3").ListenKey;
74
+ current?: import("../events/Listen3").ListenCurrent<[OfflineStoreStatus]> | undefined;
75
+ }) => import("../events/Listen3").ListenOff;
76
+ emit: import("../events/Listen3").Listener<[OfflineStoreStatus]>;
77
+ has(key: import("../events/Listen3").ListenKey): boolean;
78
+ off(keyOrCallback: import("../events/Listen3").ListenKey | import("../events/Listen3").Listener<[OfflineStoreStatus]> | null): void;
79
+ close(): void;
80
+ count(): number;
81
+ keys(): import("../events/Listen3").ListenKey[];
82
+ isRunning(): boolean;
83
+ run(): void;
84
+ onClose(cb: () => void): import("../events/Listen3").ListenOff;
85
+ };
86
+ setSyncStatus: (patch: Partial<OfflineStoreStatus>) => void;
87
+ };
88
+ export declare function createOfflineStore<T extends object>(opts: CreateOfflineStoreOpts<T>): Promise<OfflineStore<T>>;
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMemoryOfflineStorage = createMemoryOfflineStorage;
4
+ exports.persistStore = persistStore;
5
+ exports.createOfflineStore = createOfflineStore;
6
+ const common_1 = require("../core/common");
7
+ const Listen3_1 = require("../events/Listen3");
8
+ const store_1 = require("./store");
9
+ const store_replay_1 = require("./store-replay");
10
+ function isRecord(v) {
11
+ return !!v && typeof v == 'object' && typeof v.version == 'number'
12
+ && typeof v.seq == 'number' && 'snapshot' in v;
13
+ }
14
+ function cloneValue(value) {
15
+ return (0, common_1.clone)(value);
16
+ }
17
+ function createMemoryOfflineStorage(initial) {
18
+ const data = new Map();
19
+ for (const [k, v] of Object.entries(initial ?? {}))
20
+ data.set(k, cloneValue(v));
21
+ const api = {
22
+ async read(key) {
23
+ return data.has(key) ? cloneValue(data.get(key)) : undefined;
24
+ },
25
+ async write(key, value) {
26
+ data.set(key, cloneValue(value));
27
+ },
28
+ async remove(key) {
29
+ data.delete(key);
30
+ },
31
+ async transaction(fn) {
32
+ return fn(api);
33
+ },
34
+ dump() {
35
+ const out = {};
36
+ for (const [k, v] of data)
37
+ out[k] = cloneValue(v);
38
+ return out;
39
+ },
40
+ };
41
+ return api;
42
+ }
43
+ function emitError(opts, error) {
44
+ if (opts.onError)
45
+ opts.onError(error);
46
+ else
47
+ setTimeout(function rethrowOfflineStoreError() { throw error; }, 0);
48
+ }
49
+ function copyStatus(status) {
50
+ return { ...status };
51
+ }
52
+ function persistStore(store, opts) {
53
+ const { key, storage, version = 1, debounceMs = 250, now = Date.now, onStatus } = opts;
54
+ let seq = opts.seq ?? -1;
55
+ let savedAt = opts.savedAt;
56
+ let closed = false;
57
+ let dirty = false;
58
+ let timer = null;
59
+ let chain = Promise.resolve();
60
+ const status = {
61
+ ready: true,
62
+ syncing: false,
63
+ offline: false,
64
+ stale: false,
65
+ saving: false,
66
+ seq,
67
+ savedAt,
68
+ };
69
+ const [emitStatus, statusListen] = (0, Listen3_1.listenStore)({
70
+ current: () => [copyStatus(status)],
71
+ });
72
+ function updateStatus(patch = {}) {
73
+ Object.assign(status, patch, { seq, savedAt });
74
+ const snap = copyStatus(status);
75
+ emitStatus(snap);
76
+ onStatus?.(snap);
77
+ }
78
+ function clearTimer() {
79
+ if (timer)
80
+ clearTimeout(timer);
81
+ timer = null;
82
+ }
83
+ function scheduleSave() {
84
+ if (closed)
85
+ return;
86
+ dirty = true;
87
+ clearTimer();
88
+ timer = setTimeout(function flushOfflineStoreTimer() {
89
+ timer = null;
90
+ flush().catch(e => emitError(opts, e));
91
+ }, Math.max(0, debounceMs));
92
+ timer.unref?.();
93
+ }
94
+ async function writeRecord(record) {
95
+ if (storage.transaction) {
96
+ await storage.transaction(async function writeOfflineStoreTx(tx) {
97
+ await tx.write(key, record);
98
+ });
99
+ }
100
+ else {
101
+ await storage.write(key, record);
102
+ }
103
+ }
104
+ async function runFlush(force = false) {
105
+ if (closed)
106
+ return;
107
+ if (!dirty && !force)
108
+ return;
109
+ dirty = false;
110
+ updateStatus({ saving: true, error: undefined });
111
+ const nextSavedAt = now();
112
+ const record = {
113
+ version,
114
+ seq,
115
+ snapshot: store.snapshot(),
116
+ savedAt: nextSavedAt,
117
+ };
118
+ try {
119
+ await writeRecord(record);
120
+ savedAt = nextSavedAt;
121
+ updateStatus({ saving: false, error: undefined });
122
+ }
123
+ catch (e) {
124
+ updateStatus({ saving: false, error: e });
125
+ throw e;
126
+ }
127
+ finally {
128
+ if (dirty && !closed)
129
+ scheduleSave();
130
+ }
131
+ }
132
+ function flush() {
133
+ clearTimer();
134
+ const task = chain.catch(() => { }).then(function flushOfflineStore() { return runFlush(false); });
135
+ chain = task.catch(() => { });
136
+ return task;
137
+ }
138
+ function forceFlush() {
139
+ clearTimer();
140
+ dirty = true;
141
+ const task = chain.catch(() => { }).then(function forceFlushOfflineStore() { return runFlush(true); });
142
+ chain = task.catch(() => { });
143
+ return task;
144
+ }
145
+ function setSeq(nextSeq) {
146
+ if (nextSeq == seq)
147
+ return;
148
+ seq = nextSeq;
149
+ updateStatus();
150
+ scheduleSave();
151
+ }
152
+ const offStore = store.listenPaths().on(function persistStoreChange() {
153
+ scheduleSave();
154
+ });
155
+ function close() {
156
+ if (closed)
157
+ return;
158
+ closed = true;
159
+ clearTimer();
160
+ offStore();
161
+ statusListen.close();
162
+ }
163
+ function setSyncStatus(patch) {
164
+ updateStatus(patch);
165
+ }
166
+ return {
167
+ flush,
168
+ forceFlush,
169
+ close,
170
+ setSeq,
171
+ seq: () => seq,
172
+ status: () => copyStatus(status),
173
+ statusListen,
174
+ setSyncStatus,
175
+ };
176
+ }
177
+ async function loadSnapshot(opts) {
178
+ const { key, storage, initial, version = 1, migrate } = opts;
179
+ const raw = await storage.read(key);
180
+ if (raw == null)
181
+ return { snapshot: initial, seq: -1 };
182
+ if (isRecord(raw)) {
183
+ if (raw.version == version)
184
+ return { snapshot: raw.snapshot, seq: raw.seq, savedAt: raw.savedAt };
185
+ if (migrate)
186
+ return {
187
+ snapshot: await migrate(raw.snapshot, raw.version, version),
188
+ seq: raw.seq,
189
+ savedAt: raw.savedAt,
190
+ };
191
+ return { snapshot: initial, seq: -1 };
192
+ }
193
+ if (migrate)
194
+ return { snapshot: await migrate(raw, 0, version), seq: -1 };
195
+ return { snapshot: initial, seq: -1 };
196
+ }
197
+ async function createOfflineStore(opts) {
198
+ const { remote, storeOpts, syncOpts = {}, onStatus } = opts;
199
+ if (opts.mode && opts.mode != 'snapshot')
200
+ throw new Error('createOfflineStore: only snapshot mode is implemented');
201
+ let loaded;
202
+ try {
203
+ loaded = await loadSnapshot(opts);
204
+ }
205
+ catch (e) {
206
+ emitError(opts, e);
207
+ loaded = { snapshot: opts.initial, seq: -1 };
208
+ }
209
+ const store = (0, store_1.createStore)(loaded.snapshot, storeOpts);
210
+ const persist = persistStore(store, {
211
+ key: opts.key,
212
+ storage: opts.storage,
213
+ version: opts.version,
214
+ seq: loaded.seq,
215
+ savedAt: loaded.savedAt,
216
+ debounceMs: opts.debounceMs,
217
+ now: opts.now,
218
+ onError: opts.onError,
219
+ onStatus,
220
+ });
221
+ let sub = null;
222
+ let ready = Promise.resolve();
223
+ async function reconnect(nextRemote, nextSyncOpts = syncOpts) {
224
+ sub?.();
225
+ persist.setSyncStatus({ syncing: true, offline: false, ready: false, error: undefined });
226
+ const { onSeq, onError, onStale, since, ...rest } = nextSyncOpts;
227
+ const effectiveSince = since ?? (persist.seq() >= 0 ? persist.seq() : undefined);
228
+ try {
229
+ sub = (0, store_replay_1.syncStoreReplay)(store, nextRemote, {
230
+ ...rest,
231
+ since: effectiveSince,
232
+ onSeq: function offlineStoreOnSeq(seq) {
233
+ persist.setSeq(seq);
234
+ onSeq?.(seq);
235
+ },
236
+ onError: function offlineStoreOnError(error) {
237
+ persist.setSyncStatus({ offline: true, syncing: false, error });
238
+ onError?.(error);
239
+ if (!onError)
240
+ opts.onError?.(error);
241
+ },
242
+ onStale: onStale && function offlineStoreOnStale(info) {
243
+ persist.setSyncStatus({ stale: info.stale });
244
+ onStale(info);
245
+ },
246
+ });
247
+ ready = sub.ready.then(function offlineStoreReady() {
248
+ persist.setSyncStatus({ ready: true, syncing: false });
249
+ });
250
+ await ready;
251
+ }
252
+ catch (e) {
253
+ persist.setSyncStatus({ ready: true, syncing: false, offline: true, error: e });
254
+ emitError(opts, e);
255
+ }
256
+ }
257
+ if (remote) {
258
+ ready = reconnect(remote);
259
+ }
260
+ else {
261
+ persist.setSyncStatus({ ready: true, syncing: false, offline: true });
262
+ }
263
+ function close() {
264
+ sub?.();
265
+ persist.close();
266
+ }
267
+ function flush() {
268
+ return persist.flush();
269
+ }
270
+ return Object.assign(store, {
271
+ get ready() { return ready; },
272
+ close,
273
+ flush,
274
+ status: persist.status,
275
+ statusListen: persist.statusListen,
276
+ reconnect,
277
+ });
278
+ }
@@ -17,57 +17,28 @@ export declare function exposeStoreReplay<T extends object>(store: Store<T>, opt
17
17
  changedData?: any;
18
18
  };
19
19
  replay: {
20
- func: import("../..").Listener<[StorePatch]>;
20
+ emit: import("../..").Listener<[StorePatch]>;
21
21
  head: () => number;
22
22
  isStale: () => boolean;
23
23
  lastTs: () => number;
24
24
  close: () => void;
25
25
  getSince: (seq: number) => ReplayEvent<[StorePatch]>[] | undefined;
26
- line: {
27
- func: import("../..").Listener<[ReplayEvent<[StorePatch]>]>;
28
- isRun: () => boolean;
29
- run: () => void;
30
- close: () => void;
31
- eventClose: (cb: () => void) => () => void;
32
- onClose: (cb: () => void) => () => void;
33
- removeEventClose: (cb: () => void) => void;
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
- cbClose?: () => void;
38
- key?: string | symbol;
39
- }) => () => void;
40
- removeListen: (k: (string | symbol) | import("../..").Listener<[ReplayEvent<[StorePatch]>]> | null) => void;
41
- once: (cb: import("../..").Listener<[ReplayEvent<[StorePatch]>]>, opts?: {
42
- key?: string | symbol;
43
- }) => () => void;
44
- count: () => number;
45
- readonly getAllKeys: (string | symbol)[];
46
- };
26
+ line: import("../..").ListenApi<[ReplayEvent<[StorePatch]>]>;
47
27
  hasKeyframe: boolean;
48
28
  keyframe: () => ReplayEvent<[StorePatch]> | undefined;
49
29
  frame: (sinceSeq: number, hint?: unknown) => ReplayEvent<[StorePatch]>[];
50
30
  on: import("../events/replay-listen").ListenOnReplay<[StorePatch]>;
51
- addListen: (cb: import("../..").Listener<[StorePatch]>, opts?: {
52
- cbClose?: () => void;
53
- key?: string | symbol;
54
- current?: import("../..").ListenCurrent<[StorePatch]> | undefined;
55
- since?: number;
56
- onSeq?: (seq: number) => void;
57
- }) => () => void;
58
- removeListen: (k: (string | symbol) | import("../..").Listener<[StorePatch]> | null) => void;
59
31
  once: (cb: import("../..").Listener<[StorePatch]>, opts?: {
60
32
  key?: string | symbol;
61
33
  current?: import("../..").ListenCurrent<[StorePatch]> | undefined;
62
34
  }) => () => void;
63
- getAllKeys: (string | symbol)[];
64
- isRun: () => boolean;
65
- run: () => void;
66
- eventClose: (cb: () => void) => () => void;
67
- onClose: (cb: () => void) => () => void;
68
- removeEventClose: (cb: () => void) => void;
69
- off: (k: (string | symbol) | import("../..").Listener<[StorePatch]> | null) => void;
70
- count: () => number;
35
+ has(key: import("../..").ListenKey): boolean;
36
+ off(keyOrCallback: import("../..").ListenKey | import("../..").Listener<[StorePatch]> | null): void;
37
+ count(): number;
38
+ keys(): import("../..").ListenKey[];
39
+ isRunning(): boolean;
40
+ run(): void;
41
+ onClose(cb: () => void): import("../..").ListenOff;
71
42
  };
72
43
  close: () => void;
73
44
  };
@@ -34,7 +34,7 @@ function condensePatchTail(tail) {
34
34
  return [...held.values()];
35
35
  }
36
36
  function exposeStoreReplay(store, opts = {}) {
37
- const [emitPatch, lineApi] = (0, replay_listen_1.UseReplayListen)({
37
+ const [emitPatch, lineApi] = (0, replay_listen_1.replayListen)({
38
38
  current: () => [{ path: [], exists: true, value: store.snapshot() }],
39
39
  frame: condensePatchTail,
40
40
  history: opts.getSince ? undefined : (opts.history ?? 1024),
@@ -1,4 +1,4 @@
1
- import { funcListenCallbackBase } from "../events/Listen";
1
+ import { createListen } from "../events/Listen";
2
2
  import { listenUpdate, listenUpdatePaths, reactive, ReactiveChange } from "./reactive2";
3
3
  export type StorePath = readonly PropertyKey[];
4
4
  export type StoreDrain = "micro" | "immediate" | number | ((flush: () => void) => void);
@@ -83,7 +83,7 @@ export type Store<T extends object> = {
83
83
  on(cb: (value: T, ctx: StoreCtx<T>) => void, opts?: StoreSubOpts): () => void;
84
84
  once(cb: (value: T, ctx: StoreCtx<T>) => void, opts?: StoreSubOpts): () => void;
85
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]>>;
86
+ each(opts?: StoreEachOpts): ReturnType<typeof createListen<[key: string, value: T[keyof T] | undefined, ctx: StoreEachCtx]>>;
87
87
  listen(): ReturnType<typeof listenUpdate>;
88
88
  listenPaths(): ReturnType<typeof listenUpdatePaths>;
89
89
  count(): number;
@@ -292,7 +292,7 @@ function patchesForMask(patch, mask) {
292
292
  return out;
293
293
  }
294
294
  function createPatchesListen(store) {
295
- return (0, Listen_1.funcListenCallbackBase)((emit) => {
295
+ return (0, Listen_1.createListen)((emit) => {
296
296
  const off = store.listenPaths().on((change) => {
297
297
  for (const path of change.paths)
298
298
  emit(makePatch(store.state, path));
@@ -300,9 +300,9 @@ function createPatchesListen(store) {
300
300
  return off;
301
301
  }, {
302
302
  event: (type, count, api) => {
303
- if (type == "add" && count == 1 && !api.isRun())
303
+ if (type == "add" && count == 1 && !api.isRunning())
304
304
  api.run();
305
- if (type == "remove" && count == 0 && api.isRun())
305
+ if (type == "remove" && count == 0 && api.isRunning())
306
306
  api.close();
307
307
  },
308
308
  });
@@ -310,7 +310,7 @@ function createPatchesListen(store) {
310
310
  function createEachListen(store, opts = {}) {
311
311
  if (opts.depth != null && opts.depth != 1)
312
312
  throw new Error("store.each: only depth 1 is supported (reserved option)");
313
- return (0, Listen_1.funcListenCallbackBase)((emit) => {
313
+ return (0, Listen_1.createListen)((emit) => {
314
314
  const known = new Set(Reflect.ownKeys((0, reactive2_1.toRaw)(store.state)));
315
315
  function emitKey(key) {
316
316
  const raw = (0, reactive2_1.toRaw)(store.state);
@@ -342,15 +342,15 @@ function createEachListen(store, opts = {}) {
342
342
  return off;
343
343
  }, {
344
344
  event: (type, count, api) => {
345
- if (type == "add" && count == 1 && !api.isRun())
345
+ if (type == "add" && count == 1 && !api.isRunning())
346
346
  api.run();
347
- if (type == "remove" && count == 0 && api.isRun())
347
+ if (type == "remove" && count == 0 && api.isRunning())
348
348
  api.close();
349
349
  },
350
350
  });
351
351
  }
352
352
  function createChangedDataListen(store) {
353
- return (0, Listen_1.funcListenCallbackBase)((emit) => {
353
+ return (0, Listen_1.createListen)((emit) => {
354
354
  const off = store.listenPaths().on((change) => {
355
355
  const mask = maskFromPaths(change.paths);
356
356
  emit({ mask, data: pickSnapshot(store.state, mask) });
@@ -358,9 +358,9 @@ function createChangedDataListen(store) {
358
358
  return off;
359
359
  }, {
360
360
  event: (type, count, api) => {
361
- if (type == "add" && count == 1 && !api.isRun())
361
+ if (type == "add" && count == 1 && !api.isRunning())
362
362
  api.run();
363
- if (type == "remove" && count == 0 && api.isRun())
363
+ if (type == "remove" && count == 0 && api.isRunning())
364
364
  api.close();
365
365
  },
366
366
  });
@@ -588,16 +588,12 @@ function exposeStore(store, opts = {}) {
588
588
  return api;
589
589
  }
590
590
  function isRemoteListen(listen) {
591
- return typeof listen?.on == "function" || typeof listen?.addListen == "function";
591
+ return typeof listen?.on == "function";
592
592
  }
593
593
  function subscribeRemote(listen, cb) {
594
- let handle;
595
- if (typeof listen?.on == "function")
596
- handle = listen.on(cb);
597
- else if (typeof listen?.addListen == "function")
598
- handle = listen.addListen(cb);
599
- else
594
+ if (typeof listen?.on != "function")
600
595
  return () => { };
596
+ const handle = listen.on(cb);
601
597
  return () => {
602
598
  if (typeof handle == "function")
603
599
  handle();
@@ -605,8 +601,6 @@ function subscribeRemote(listen, cb) {
605
601
  handle.off();
606
602
  else if (typeof listen?.off == "function")
607
603
  listen.off(cb);
608
- else if (typeof listen?.removeListen == "function")
609
- listen.removeListen(cb);
610
604
  };
611
605
  }
612
606
  function createStoreMirror(remote, initial = {}, opts = {}) {