@flurryx/store 0.8.4 → 1.0.1

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/dist/index.d.ts CHANGED
@@ -1,5 +1,176 @@
1
1
  import { Signal, InjectionToken } from '@angular/core';
2
- import { ResourceState, KeyedResourceKey } from '@flurryx/core';
2
+ import { ResourceState, KeyedResourceData, KeyedResourceKey } from '@flurryx/core';
3
+
4
+ /** Message produced by `store.update(key, partial)` — merges partial state into a slot. */
5
+ type UpdateStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
6
+ readonly [K in TKey]: {
7
+ readonly type: "update";
8
+ readonly key: K;
9
+ readonly state: Partial<TData[K]>;
10
+ };
11
+ }[TKey];
12
+ /** Message produced by `store.clear(key)` — resets a single slot to its initial state. */
13
+ type ClearStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
14
+ readonly [K in TKey]: {
15
+ readonly type: "clear";
16
+ readonly key: K;
17
+ };
18
+ }[TKey];
19
+ /** Message produced by `store.clearAll()` — resets every slot in the store. */
20
+ interface ClearAllStoreMessage<TData extends StoreDataShape<TData>> {
21
+ readonly type: "clearAll";
22
+ }
23
+ /** Message produced by `store.startLoading(key)` — sets `isLoading: true` and clears `status`/`errors`. */
24
+ type StartLoadingStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
25
+ readonly [K in TKey]: {
26
+ readonly type: "startLoading";
27
+ readonly key: K;
28
+ };
29
+ }[TKey];
30
+ /** Message produced by `store.stopLoading(key)` — sets `isLoading: false`. */
31
+ type StopLoadingStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
32
+ readonly [K in TKey]: {
33
+ readonly type: "stopLoading";
34
+ readonly key: K;
35
+ };
36
+ }[TKey];
37
+ /** Message produced by `store.updateKeyedOne(key, resourceKey, entity)` — merges a single entity into a keyed slot. */
38
+ type UpdateKeyedOneStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
39
+ readonly [K in KeyedStoreKey<TData, TKey>]: {
40
+ readonly type: "updateKeyedOne";
41
+ readonly key: K;
42
+ readonly resourceKey: KeyedResourceEntryKey<TData, K>;
43
+ readonly entity: KeyedResourceEntryValue<TData, K>;
44
+ };
45
+ }[KeyedStoreKey<TData, TKey>];
46
+ /** Message produced by `store.clearKeyedOne(key, resourceKey)` — removes a single entity from a keyed slot. */
47
+ type ClearKeyedOneStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
48
+ readonly [K in KeyedStoreKey<TData, TKey>]: {
49
+ readonly type: "clearKeyedOne";
50
+ readonly key: K;
51
+ readonly resourceKey: KeyedResourceEntryKey<TData, K>;
52
+ };
53
+ }[KeyedStoreKey<TData, TKey>];
54
+ /** Message produced by `store.startKeyedLoading(key, resourceKey)` — marks a single entity as loading. */
55
+ type StartKeyedLoadingStoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
56
+ readonly [K in KeyedStoreKey<TData, TKey>]: {
57
+ readonly type: "startKeyedLoading";
58
+ readonly key: K;
59
+ readonly resourceKey: KeyedResourceEntryKey<TData, K>;
60
+ };
61
+ }[KeyedStoreKey<TData, TKey>];
62
+ /** Discriminated union of all typed store messages published to the broker channel. */
63
+ type StoreMessage<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = UpdateStoreMessage<TData, TKey> | ClearStoreMessage<TData, TKey> | ClearAllStoreMessage<TData> | StartLoadingStoreMessage<TData, TKey> | StopLoadingStoreMessage<TData, TKey> | UpdateKeyedOneStoreMessage<TData, TKey> | ClearKeyedOneStoreMessage<TData, TKey> | StartKeyedLoadingStoreMessage<TData, TKey>;
64
+ /** Full store state captured at a point in time, keyed by slot name. Used by history and time travel. */
65
+ type StoreSnapshot<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = Partial<{
66
+ readonly [K in TKey]: TData[K];
67
+ }>;
68
+ /** Delivery status of a broker message: `"pending"` → `"acknowledged"` or `"dead-letter"`. */
69
+ type StoreMessageStatus = "pending" | "acknowledged" | "dead-letter";
70
+
71
+ /**
72
+ * Persisted broker message record stored in the active message channel.
73
+ *
74
+ * Message ids are allocated when the message is first published and remain stable
75
+ * across acknowledgement, replay, and dead-letter transitions.
76
+ */
77
+ interface StoreMessageRecord<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
78
+ /** Stable message id used by `replay(...)` and dead-letter recovery APIs. */
79
+ readonly id: number;
80
+ /** Published store message payload. */
81
+ readonly message: StoreMessage<TData, TKey>;
82
+ /** Latest delivery status stored by the broker channel. */
83
+ readonly status: StoreMessageStatus;
84
+ /** Number of delivery attempts made for this message. */
85
+ readonly attempts: number;
86
+ /** Timestamp when the message was first published to the channel. */
87
+ readonly createdAt: number;
88
+ /** Timestamp of the most recent delivery attempt. */
89
+ readonly lastAttemptedAt: number | null;
90
+ /** Timestamp of the most recent successful acknowledgement, if any. */
91
+ readonly acknowledgedAt: number | null;
92
+ /** Last recorded delivery error, or `null` when the latest attempt succeeded. */
93
+ readonly error: string | null;
94
+ }
95
+ /** Minimal string-based storage adapter used by storage-backed message channels. */
96
+ interface StoreMessageChannelStorage {
97
+ getItem(key: string): string | null;
98
+ setItem(key: string, value: string): void;
99
+ removeItem(key: string): void;
100
+ }
101
+ /**
102
+ * Pluggable persistence channel used by the broker to store published messages.
103
+ *
104
+ * The default channel is in-memory, but storage-backed or custom providers can be
105
+ * supplied to keep messages available across refreshes or offline sessions.
106
+ */
107
+ interface StoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
108
+ /** Stores a newly published message and allocates its stable message id. */
109
+ publish(message: StoreMessage<TData, TKey>): StoreMessageRecord<TData, TKey>;
110
+ /** Reads a single persisted message record by id. */
111
+ getMessage(id: number): StoreMessageRecord<TData, TKey> | undefined;
112
+ /** Reads every persisted message record from the channel. */
113
+ getMessages(): readonly StoreMessageRecord<TData, TKey>[];
114
+ /** Persists a new state for an existing message record. */
115
+ saveMessage(entry: StoreMessageRecord<TData, TKey>): void;
116
+ }
117
+ /** Optional store configuration used to override the default in-memory message channel. */
118
+ interface StoreMessageChannelOptions<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
119
+ readonly channel?: StoreMessageChannel<TData, TKey>;
120
+ }
121
+ /** Options for {@link createCompositeStoreMessageChannel}. The first channel is the primary (reads + id allocation); replicas receive all writes. */
122
+ interface CompositeStoreMessageChannelOptions<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
123
+ readonly channels: readonly StoreMessageChannel<TData, TKey>[];
124
+ }
125
+ /** Options for {@link createStorageStoreMessageChannel}. Provide a custom storage adapter, key, and optional serialize/deserialize hooks. */
126
+ interface StorageStoreMessageChannelOptions<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
127
+ readonly storage: StoreMessageChannelStorage;
128
+ readonly storageKey: string;
129
+ readonly serialize?: (state: PersistedStoreMessageChannelState<TData, TKey>) => string;
130
+ readonly deserialize?: (value: string) => PersistedStoreMessageChannelState<TData, TKey>;
131
+ }
132
+ /** Options for {@link createLocalStorageStoreMessageChannel} and {@link createSessionStorageStoreMessageChannel}. Storage defaults to the browser global. */
133
+ interface BrowserStorageStoreMessageChannelOptions<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> extends Omit<StorageStoreMessageChannelOptions<TData, TKey>, "storage"> {
134
+ readonly storage?: StoreMessageChannelStorage;
135
+ }
136
+ interface PersistedStoreMessageChannelState<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
137
+ readonly nextId: number;
138
+ readonly messages: readonly StoreMessageRecord<TData, TKey>[];
139
+ }
140
+ /**
141
+ * Creates an in-memory message channel. Messages are stored in a JavaScript array
142
+ * and lost on page refresh. This is the default channel when no `channel` option is provided.
143
+ */
144
+ declare function createInMemoryStoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>>(): StoreMessageChannel<TData, TKey>;
145
+ /**
146
+ * Creates a message channel backed by a custom storage adapter.
147
+ * Messages are serialized and persisted via the provided `storage` object.
148
+ * When the storage quota is exceeded, the oldest messages are evicted automatically.
149
+ *
150
+ * @param options - Storage adapter, key, and optional serialize/deserialize hooks.
151
+ */
152
+ declare function createStorageStoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>>(options: StorageStoreMessageChannelOptions<TData, TKey>): StoreMessageChannel<TData, TKey>;
153
+ /**
154
+ * Creates a message channel backed by `localStorage`.
155
+ * Messages survive page refreshes and browser restarts (same-origin only).
156
+ *
157
+ * @param options - Storage key and optional serialize/deserialize hooks.
158
+ */
159
+ declare function createLocalStorageStoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>>(options: BrowserStorageStoreMessageChannelOptions<TData, TKey>): StoreMessageChannel<TData, TKey>;
160
+ /**
161
+ * Creates a message channel backed by `sessionStorage`.
162
+ * Messages survive page refreshes but are lost when the browser tab closes.
163
+ *
164
+ * @param options - Storage key and optional serialize/deserialize hooks.
165
+ */
166
+ declare function createSessionStorageStoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>>(options: BrowserStorageStoreMessageChannelOptions<TData, TKey>): StoreMessageChannel<TData, TKey>;
167
+ /**
168
+ * Creates a composite message channel that fans out writes to multiple channels.
169
+ * The first channel is the primary (handles reads and id allocation); all channels receive writes.
170
+ *
171
+ * @param options - Array of channels. Must contain at least one.
172
+ */
173
+ declare function createCompositeStoreMessageChannel<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>>(options: CompositeStoreMessageChannelOptions<TData, TKey>): StoreMessageChannel<TData, TKey>;
3
174
 
4
175
  /**
5
176
  * Constraint type ensuring every key in a store data map holds a {@link ResourceState}.
@@ -11,6 +182,16 @@ type StoreDataShape<TData> = {
11
182
  * Extracts the string-typed keys from a store data map.
12
183
  */
13
184
  type StoreKey<TData> = keyof TData & string;
185
+ /** Extracts the value stored inside a store slot's `ResourceState<T>`. */
186
+ type StoreResourceValue<TData extends StoreDataShape<TData>, K extends StoreKey<TData>> = TData[K] extends ResourceState<infer TValue> ? TValue : never;
187
+ /** Narrows store keys whose `data` payload is a keyed resource map. */
188
+ type KeyedStoreKey<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> = {
189
+ [K in TKey]: StoreResourceValue<TData, K> extends KeyedResourceData<infer _TResourceKey, infer _TValue> ? K : never;
190
+ }[TKey];
191
+ /** Extracts the resource key type for a keyed store slot. */
192
+ type KeyedResourceEntryKey<TData extends StoreDataShape<TData>, K extends KeyedStoreKey<TData>> = StoreResourceValue<TData, K> extends KeyedResourceData<infer TResourceKey, unknown> ? TResourceKey : never;
193
+ /** Extracts the entity value type for a keyed store slot. */
194
+ type KeyedResourceEntryValue<TData extends StoreDataShape<TData>, K extends KeyedStoreKey<TData>> = StoreResourceValue<TData, K> extends KeyedResourceData<KeyedResourceKey, infer TValue> ? TValue : never;
14
195
  /**
15
196
  * Phantom-typed marker for a store resource slot.
16
197
  * Carries type information at compile time with zero runtime cost.
@@ -44,6 +225,15 @@ type InferData<TConfig extends StoreConfig> = {
44
225
  type ConfigToData<TConfig extends object> = {
45
226
  [K in keyof TConfig & string]: ResourceState<TConfig[K]>;
46
227
  };
228
+ /**
229
+ * Optional runtime configuration for a store instance.
230
+ *
231
+ * The default channel is in-memory. Supply `channel` to persist broker messages
232
+ * elsewhere, such as local storage, session storage, a composite channel, or a
233
+ * custom provider.
234
+ */
235
+ interface StoreOptions<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> extends StoreMessageChannelOptions<TData, TKey> {
236
+ }
47
237
  /**
48
238
  * Shared store interface implemented by both BaseStore and LazyStore.
49
239
  *
@@ -63,6 +253,48 @@ interface IStore<TData extends StoreDataShape<TData>> {
63
253
  startLoading<K extends StoreKey<TData>>(key: K): void;
64
254
  /** Marks a slot as no longer loading: sets `isLoading: false`. */
65
255
  stopLoading<K extends StoreKey<TData>>(key: K): void;
256
+ /**
257
+ * Re-executes previously published channel message id(s).
258
+ *
259
+ * Unlike `travelTo(...)`, replay goes back through the broker/consumer path,
260
+ * so it can mutate the store again, truncate future history after time
261
+ * travel, and record new acknowledged history entries.
262
+ */
263
+ replay: StoreHistory<TData>["replay"];
264
+ /**
265
+ * Restores the store to a previously recorded history index.
266
+ *
267
+ * This navigates snapshots only and does not re-run any message.
268
+ */
269
+ travelTo: StoreHistory<TData>["travelTo"];
270
+ /** Moves one step backward in the recorded snapshot history when possible. */
271
+ undo: StoreHistory<TData>["undo"];
272
+ /** Moves one step forward in the recorded snapshot history when possible. */
273
+ redo: StoreHistory<TData>["redo"];
274
+ /**
275
+ * Returns a defensive copy of the recorded history.
276
+ *
277
+ * The first entry is always the initial snapshot captured when the store was created.
278
+ */
279
+ getHistory: StoreHistory<TData>["getHistory"];
280
+ /**
281
+ * Returns persisted broker message records from the configured channel.
282
+ *
283
+ * Use `getMessages(key)` to inspect only the messages that affected one store key.
284
+ */
285
+ getMessages: StoreHistory<TData>["getMessages"];
286
+ /**
287
+ * Returns messages that failed broker acknowledgement.
288
+ *
289
+ * These entries can be inspected for diagnostics and retried later.
290
+ */
291
+ getDeadLetters: StoreHistory<TData>["getDeadLetters"];
292
+ /** Replays a single dead-letter message by dead-letter id. */
293
+ replayDeadLetter: StoreHistory<TData>["replayDeadLetter"];
294
+ /** Attempts to replay all current dead-letter messages once. */
295
+ replayDeadLetters: StoreHistory<TData>["replayDeadLetters"];
296
+ /** Returns the currently restored snapshot index used by `travelTo`, `undo`, and `redo`. */
297
+ getCurrentIndex: StoreHistory<TData>["getCurrentIndex"];
66
298
  /** Merges a single entity into a keyed slot and sets its status to `'Success'`. */
67
299
  updateKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
68
300
  /** Removes a single entity (and its loading/status/errors) from a keyed slot. */
@@ -74,6 +306,163 @@ interface IStore<TData extends StoreDataShape<TData>> {
74
306
  * @returns A cleanup function that removes the listener.
75
307
  */
76
308
  onUpdate<K extends StoreKey<TData>>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
309
+ /** Reactive signal containing the full history entries. */
310
+ readonly history: Signal<readonly StoreHistoryEntry<TData>[]>;
311
+ /** Reactive signal containing all channel message records. */
312
+ readonly messages: Signal<readonly StoreMessageRecord<TData>[]>;
313
+ /** Reactive signal containing the current history index. */
314
+ readonly currentIndex: Signal<number>;
315
+ /** Reactive signal containing all registered store keys. */
316
+ readonly keys: Signal<readonly StoreKey<TData>[]>;
317
+ }
318
+
319
+ /**
320
+ * Creates a deep clone of the given value.
321
+ *
322
+ * **Warning:** Class instances with constructor logic, private fields, or
323
+ * non-enumerable state will not clone correctly. The clone preserves the
324
+ * prototype chain via `Object.create(Object.getPrototypeOf(...))` but does
325
+ * **not** invoke the constructor, so any side-effects or hidden state set
326
+ * during construction will be missing from the clone.
327
+ */
328
+ declare function cloneValue<T>(value: T): T;
329
+ /**
330
+ * Creates a minimal patch object that, when spread onto `currentState`, produces `snapshotState`.
331
+ * Properties present in the snapshot are cloned; properties absent from the snapshot are set to `undefined`.
332
+ *
333
+ * @param currentState - The current slot state.
334
+ * @param snapshotState - The target snapshot state to restore.
335
+ * @returns A partial state object suitable for `Signal.update()`.
336
+ */
337
+ declare function createSnapshotRestorePatch<TState extends ResourceState<unknown>>(currentState: TState, snapshotState: TState): Partial<TState>;
338
+
339
+ /**
340
+ * Single acknowledged point in store history.
341
+ *
342
+ * Entry `0` is always the initial snapshot captured when the history driver is
343
+ * created, so its `id`, `message`, and `acknowledgedAt` are `null`.
344
+ */
345
+ interface StoreHistoryEntry<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
346
+ /** Stable message id used by `replay(...)`; `null` for the initial snapshot entry. */
347
+ readonly id: number | null;
348
+ /** Snapshot position used by `travelTo(index)`, `undo()`, and `redo()`. */
349
+ readonly index: number;
350
+ /** Acknowledged message that produced this snapshot; `null` for the initial entry. */
351
+ readonly message: StoreMessage<TData, TKey> | null;
352
+ /** Full store snapshot captured immediately after the message was acknowledged. */
353
+ readonly snapshot: StoreSnapshot<TData, TKey>;
354
+ /** Acknowledgement timestamp for the message; `null` for the initial snapshot entry. */
355
+ readonly acknowledgedAt: number | null;
356
+ }
357
+ /**
358
+ * Failed message tracked by the internal broker when the store consumer does not
359
+ * acknowledge a published message.
360
+ */
361
+ interface StoreDeadLetterEntry<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
362
+ /** Stable dead-letter id used by `replayDeadLetter(id)`. */
363
+ readonly id: number;
364
+ /** Original message that failed acknowledgement. */
365
+ readonly message: StoreMessage<TData, TKey>;
366
+ /** Number of failed acknowledgement attempts for this dead letter. */
367
+ readonly attempts: number;
368
+ /** Last acknowledgement error captured for this dead letter. */
369
+ readonly error: string;
370
+ /** Timestamp of the most recent failure. */
371
+ readonly failedAt: number;
372
+ }
373
+ /**
374
+ * Public history and recovery API exposed on every store instance.
375
+ *
376
+ * `travelTo(...)` navigates snapshots by history index, while `replay(...)`
377
+ * re-executes previously published channel messages by their stable message ids.
378
+ */
379
+ interface StoreHistory<TData extends StoreDataShape<TData>, TKey extends StoreKey<TData> = StoreKey<TData>> {
380
+ /**
381
+ * Re-executes one previously published message by id.
382
+ *
383
+ * The message does not need to have been acknowledged before. Replay resolves it
384
+ * from the configured message channel, sends it back through the broker/consumer
385
+ * flow, and may create a fresh acknowledged history entry if the replay succeeds.
386
+ *
387
+ * @throws {Error} When the id does not point to a persisted channel message.
388
+ * @returns Number of successfully acknowledged replayed messages.
389
+ */
390
+ replay(id: number): number;
391
+ /**
392
+ * Re-executes multiple previously published messages in the provided order.
393
+ *
394
+ * Every id must resolve to a persisted channel message. Replay stops with an error
395
+ * if any supplied id is invalid.
396
+ *
397
+ * @throws {Error} When any id does not point to a persisted channel message.
398
+ * @returns Number of successfully acknowledged replayed messages.
399
+ */
400
+ replay(ids: readonly number[]): number;
401
+ /**
402
+ * Restores the store to the snapshot recorded at a specific history index.
403
+ *
404
+ * This is snapshot navigation only. It does not publish or acknowledge any
405
+ * message and does not create a new history entry.
406
+ *
407
+ * @throws {Error} When the index is outside the recorded history range.
408
+ */
409
+ travelTo(index: number): void;
410
+ /**
411
+ * Moves to the previous recorded snapshot.
412
+ *
413
+ * Equivalent to `travelTo(getCurrentIndex() - 1)` when possible.
414
+ *
415
+ * @returns `true` when the pointer moved, otherwise `false` at the initial snapshot.
416
+ */
417
+ undo(): boolean;
418
+ /**
419
+ * Moves to the next recorded snapshot when history exists ahead of the current pointer.
420
+ *
421
+ * Equivalent to `travelTo(getCurrentIndex() + 1)` when possible.
422
+ *
423
+ * @returns `true` when the pointer moved, otherwise `false` at the latest snapshot.
424
+ */
425
+ redo(): boolean;
426
+ /** Returns a defensive copy of every recorded history entry, including the initial snapshot entry. */
427
+ getHistory(): readonly StoreHistoryEntry<TData, TKey>[];
428
+ /**
429
+ * Returns the history entries that affected a specific store key.
430
+ *
431
+ * The initial snapshot entry is always included as the first item. Messages such
432
+ * as `clearAll` that affect every key are included in every filtered view.
433
+ */
434
+ getHistory<K extends TKey>(key: K): readonly StoreHistoryEntry<TData, K>[];
435
+ /** Returns a defensive copy of the current dead-letter collection. */
436
+ getDeadLetters(): readonly StoreDeadLetterEntry<TData, TKey>[];
437
+ /** Returns every message currently stored in the configured channel. */
438
+ getMessages(): readonly StoreMessageRecord<TData, TKey>[];
439
+ /** Returns channel messages that affected a specific store key. */
440
+ getMessages<K extends TKey>(key: K): readonly StoreMessageRecord<TData, K>[];
441
+ /**
442
+ * Republishes a single dead-letter message by dead-letter id.
443
+ *
444
+ * On success the dead letter is removed and a new acknowledged history entry is recorded.
445
+ *
446
+ * @returns `true` when the dead letter was acknowledged on replay, otherwise `false`.
447
+ */
448
+ replayDeadLetter(id: number): boolean;
449
+ /**
450
+ * Attempts to republish every current dead letter once.
451
+ *
452
+ * Successfully acknowledged dead letters are removed. Failures remain in the
453
+ * dead-letter collection with incremented attempt counts.
454
+ *
455
+ * @returns Number of dead letters successfully acknowledged during the replay attempt.
456
+ */
457
+ replayDeadLetters(): number;
458
+ /** Returns the currently restored history index used by snapshot navigation. */
459
+ getCurrentIndex(): number;
460
+ /** Reactive signal containing the full history entries. */
461
+ readonly historySignal: Signal<readonly StoreHistoryEntry<TData, TKey>[]>;
462
+ /** Reactive signal containing all channel message records. */
463
+ readonly messagesSignal: Signal<readonly StoreMessageRecord<TData, TKey>[]>;
464
+ /** Reactive signal containing the current history index. */
465
+ readonly currentIndexSignal: Signal<number>;
77
466
  }
78
467
 
79
468
  /**
@@ -88,19 +477,55 @@ interface IStore<TData extends StoreDataShape<TData>> {
88
477
  * @template TEnum - Record mapping slot names to their string/number keys.
89
478
  * @template TData - Record mapping slot names to `ResourceState<T>` types.
90
479
  */
91
- declare abstract class BaseStore<TEnum extends Record<string, string | number>, TData extends {
480
+ declare abstract class BaseStore<TEnum extends Record<string, string | number>, TData extends StoreDataShape<TData> & {
92
481
  [K in keyof TEnum]: ResourceState<unknown>;
93
482
  }> implements IStore<TData> {
94
483
  protected readonly storeEnum: TEnum;
95
484
  private readonly signalsState;
96
- protected constructor(storeEnum: TEnum);
485
+ private readonly storeKeys;
486
+ private readonly historyDriver;
487
+ /** @inheritDoc */
488
+ readonly travelTo: (index: number) => void;
489
+ /** @inheritDoc */
490
+ readonly undo: () => boolean;
491
+ /** @inheritDoc */
492
+ readonly redo: () => boolean;
493
+ /** @inheritDoc */
494
+ readonly getDeadLetters: () => readonly StoreDeadLetterEntry<TData, StoreKey<TData>>[];
495
+ /** @inheritDoc */
496
+ readonly replayDeadLetter: (id: number) => boolean;
497
+ /** @inheritDoc */
498
+ readonly replayDeadLetters: () => number;
499
+ /** @inheritDoc */
500
+ readonly getCurrentIndex: () => number;
501
+ /** @inheritDoc */
502
+ readonly history: Signal<readonly StoreHistoryEntry<TData>[]>;
503
+ /** @inheritDoc */
504
+ readonly messages: Signal<readonly StoreMessageRecord<TData>[]>;
505
+ /** @inheritDoc */
506
+ readonly currentIndex: Signal<number>;
507
+ /** @inheritDoc */
508
+ readonly keys: Signal<readonly StoreKey<TData>[]>;
509
+ /** @inheritDoc */
510
+ replay(id: number): number;
511
+ /** @inheritDoc */
512
+ replay(ids: readonly number[]): number;
513
+ /** @inheritDoc */
514
+ getHistory(): readonly StoreHistoryEntry<TData>[];
515
+ /** @inheritDoc */
516
+ getHistory<K extends StoreKey<TData>>(key: K): readonly StoreHistoryEntry<TData, K>[];
517
+ /** @inheritDoc */
518
+ getMessages(): readonly StoreMessageRecord<TData>[];
519
+ /** @inheritDoc */
520
+ getMessages<K extends StoreKey<TData>>(key: K): readonly StoreMessageRecord<TData, K>[];
521
+ protected constructor(storeEnum: TEnum, options?: StoreOptions<TData>);
97
522
  /**
98
523
  * Returns a **read-only** `Signal` for the given store slot.
99
524
  *
100
525
  * @param key - The slot name to read.
101
526
  * @returns A `Signal` wrapping the slot's current {@link ResourceState}.
102
527
  */
103
- get<K extends keyof TData>(key: K): Signal<TData[K]>;
528
+ get<K extends StoreKey<TData>>(key: K): Signal<TData[K]>;
104
529
  /**
105
530
  * Registers a callback fired after every `update` or `clear` on the given slot.
106
531
  *
@@ -108,14 +533,14 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
108
533
  * @param callback - Receives the new state and the previous state.
109
534
  * @returns A cleanup function that removes the listener when called.
110
535
  */
111
- onUpdate<K extends keyof TData>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
536
+ onUpdate<K extends StoreKey<TData>>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
112
537
  /**
113
538
  * Partially updates a slot by merging `newState` into the current value (immutable spread).
114
539
  *
115
540
  * @param key - The slot to update.
116
541
  * @param newState - Partial state to merge (e.g. `{ data: newData, status: 'Success' }`).
117
542
  */
118
- update<K extends keyof TData>(key: K, newState: Partial<TData[K]>): void;
543
+ update<K extends StoreKey<TData>>(key: K, newState: Partial<TData[K]>): void;
119
544
  /** Resets every slot in this store to its initial idle state. */
120
545
  clearAll(): void;
121
546
  /**
@@ -123,20 +548,20 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
123
548
  *
124
549
  * @param key - The slot to clear.
125
550
  */
126
- clear<K extends keyof TData>(key: K): void;
551
+ clear<K extends StoreKey<TData>>(key: K): void;
127
552
  /**
128
553
  * Marks a slot as loading: sets `isLoading: true` and clears `status` and `errors`.
129
554
  *
130
555
  * @param key - The slot to mark as loading.
131
556
  */
132
- startLoading<K extends keyof TData>(key: K): void;
557
+ startLoading<K extends StoreKey<TData>>(key: K): void;
133
558
  /**
134
559
  * Marks a slot as no longer loading: sets `isLoading: false`.
135
560
  * Does **not** clear `status` or `errors`.
136
561
  *
137
562
  * @param key - The slot to stop loading.
138
563
  */
139
- stopLoading<K extends keyof TData>(key: K): void;
564
+ stopLoading<K extends StoreKey<TData>>(key: K): void;
140
565
  /**
141
566
  * Merges a single entity into a {@link KeyedResourceData} slot.
142
567
  * Sets its status to `'Success'` and clears per-key errors.
@@ -146,7 +571,7 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
146
571
  * @param resourceKey - The entity identifier (e.g. `'inv-123'`).
147
572
  * @param entity - The entity value to store.
148
573
  */
149
- updateKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
574
+ updateKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
150
575
  /**
151
576
  * Removes a single entity from a {@link KeyedResourceData} slot,
152
577
  * including its loading flag, status, and errors.
@@ -155,7 +580,7 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
155
580
  * @param key - The keyed slot name.
156
581
  * @param resourceKey - The entity identifier to remove.
157
582
  */
158
- clearKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
583
+ clearKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
159
584
  /**
160
585
  * Marks a single entity within a keyed slot as loading.
161
586
  * Clears its status and errors. If the slot data is not yet a {@link KeyedResourceData},
@@ -164,7 +589,7 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
164
589
  * @param key - The keyed slot name.
165
590
  * @param resourceKey - The entity identifier to mark as loading.
166
591
  */
167
- startKeyedLoading<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
592
+ startKeyedLoading<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
168
593
  private notifyUpdateHooks;
169
594
  private initializeState;
170
595
  }
@@ -177,17 +602,62 @@ declare abstract class BaseStore<TEnum extends Record<string, string | number>,
177
602
  declare class LazyStore<TData extends StoreDataShape<TData>> implements IStore<TData> {
178
603
  private readonly signals;
179
604
  private readonly hooks;
180
- constructor();
605
+ private readonly historyDriver;
606
+ /** @inheritDoc */
607
+ readonly travelTo: (index: number) => void;
608
+ /** @inheritDoc */
609
+ readonly undo: () => boolean;
610
+ /** @inheritDoc */
611
+ readonly redo: () => boolean;
612
+ /** @inheritDoc */
613
+ getMessages(): readonly StoreMessageRecord<TData>[];
614
+ /** @inheritDoc */
615
+ getMessages<K extends StoreKey<TData>>(key: K): readonly StoreMessageRecord<TData, K>[];
616
+ readonly getDeadLetters: () => readonly StoreDeadLetterEntry<TData, StoreKey<TData>>[];
617
+ /** @inheritDoc */
618
+ readonly replayDeadLetter: (id: number) => boolean;
619
+ /** @inheritDoc */
620
+ readonly replayDeadLetters: () => number;
621
+ /** @inheritDoc */
622
+ readonly getCurrentIndex: () => number;
623
+ /** @inheritDoc */
624
+ readonly history: Signal<readonly StoreHistoryEntry<TData>[]>;
625
+ /** @inheritDoc */
626
+ readonly messages: Signal<readonly StoreMessageRecord<TData>[]>;
627
+ /** @inheritDoc */
628
+ readonly currentIndex: Signal<number>;
629
+ /** @inheritDoc */
630
+ readonly keys: Signal<readonly StoreKey<TData>[]>;
631
+ private readonly keysSignal;
632
+ /** @inheritDoc */
633
+ replay(id: number): number;
634
+ /** @inheritDoc */
635
+ replay(ids: readonly number[]): number;
636
+ /** @inheritDoc */
637
+ getHistory(): readonly StoreHistoryEntry<TData>[];
638
+ /** @inheritDoc */
639
+ getHistory<K extends StoreKey<TData>>(key: K): readonly StoreHistoryEntry<TData, K>[];
640
+ constructor(options?: StoreOptions<TData>);
181
641
  private getOrCreate;
642
+ /** @inheritDoc */
182
643
  get<K extends StoreKey<TData>>(key: K): Signal<TData[K]>;
644
+ /** @inheritDoc */
183
645
  update<K extends StoreKey<TData>>(key: K, newState: Partial<TData[K]>): void;
646
+ /** @inheritDoc */
184
647
  clear<K extends StoreKey<TData>>(key: K): void;
648
+ /** @inheritDoc */
185
649
  clearAll(): void;
650
+ /** @inheritDoc */
186
651
  startLoading<K extends StoreKey<TData>>(key: K): void;
652
+ /** @inheritDoc */
187
653
  stopLoading<K extends StoreKey<TData>>(key: K): void;
654
+ /** @inheritDoc */
188
655
  updateKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
656
+ /** @inheritDoc */
189
657
  clearKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
658
+ /** @inheritDoc */
190
659
  startKeyedLoading<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
660
+ /** @inheritDoc */
191
661
  onUpdate<K extends StoreKey<TData>>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
192
662
  private notifyHooks;
193
663
  }
@@ -196,6 +666,19 @@ declare class LazyStore<TData extends StoreDataShape<TData>> implements IStore<T
196
666
  * Intermediate builder step after .resource('key') — awaits .as<T>().
197
667
  */
198
668
  interface AsStep<TAccum extends StoreConfig, TKey extends string> {
669
+ /**
670
+ * Assign the resource value type for the previously declared key.
671
+ *
672
+ * Returns the main fluent builder so you can define more resources, configure
673
+ * mirrors, or call `.build()`.
674
+ *
675
+ * @example
676
+ * ```ts
677
+ * const CustomersStore = Store
678
+ * .resource('CUSTOMERS').as<Customer[]>()
679
+ * .build();
680
+ * ```
681
+ */
199
682
  as<T>(): StoreBuilder<TAccum & Record<TKey, ResourceDef<T>>>;
200
683
  }
201
684
  /**
@@ -236,12 +719,17 @@ interface StoreBuilder<TAccum extends StoreConfig> {
236
719
  * Finalize the builder and create an `InjectionToken` (`providedIn: 'root'`).
237
720
  * All mirrors are wired up automatically when Angular creates the store.
238
721
  */
239
- build(): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
722
+ build(options?: StoreOptions<InferData<TAccum>>): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
240
723
  }
241
724
  /** Keys from the enum that have NOT yet been defined. */
242
725
  type Remaining<TEnum extends Record<string, string>, TAccum extends StoreConfig> = Exclude<keyof TEnum & string, keyof TAccum>;
243
726
  /** Intermediate .as<T>() step for the constrained builder. */
244
727
  interface ConstrainedAsStep<TEnum extends Record<string, string>, TAccum extends StoreConfig, TKey extends string> {
728
+ /**
729
+ * Assign the resource value type for the selected enum key.
730
+ *
731
+ * Returns the constrained builder for the remaining enum keys.
732
+ */
245
733
  as<T>(): ConstrainedBuilder<TEnum, TAccum & Record<TKey, ResourceDef<T>>>;
246
734
  }
247
735
  /**
@@ -249,22 +737,94 @@ interface ConstrainedAsStep<TEnum extends Record<string, string>, TAccum extends
249
737
  * defined yet. `.build()` is only available when all keys are accounted for.
250
738
  */
251
739
  type ConstrainedBuilder<TEnum extends Record<string, string>, TAccum extends StoreConfig> = [Remaining<TEnum, TAccum>] extends [never] ? {
740
+ /**
741
+ * Mirror a slot from another store. When the source updates, the target is kept in sync.
742
+ *
743
+ * @param source - The source store's `InjectionToken`.
744
+ * @param sourceKey - The key to watch on the source store.
745
+ * @param targetKey - The key on this store to write to. Defaults to `sourceKey`.
746
+ */
252
747
  mirror<TSourceData extends StoreDataShape<TSourceData>>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, targetKey?: StoreKey<TAccum>): ConstrainedBuilder<TEnum, TAccum>;
748
+ /**
749
+ * Mirror one slot to another within the same store.
750
+ * Source and target keys must be different.
751
+ *
752
+ * @param sourceKey - The slot to read from.
753
+ * @param targetKey - The slot to write to.
754
+ */
253
755
  mirrorSelf(sourceKey: StoreKey<TAccum>, targetKey: StoreKey<TAccum>): ConstrainedBuilder<TEnum, TAccum>;
756
+ /**
757
+ * Accumulate single-entity fetches from a source store into a keyed slot.
758
+ *
759
+ * @param source - The source store's `InjectionToken`.
760
+ * @param sourceKey - The single-entity key on the source store.
761
+ * @param options - Must include `extractId` to derive the entity's key from its data.
762
+ * @param targetKey - The keyed slot on this store. Defaults to `sourceKey`.
763
+ */
254
764
  mirrorKeyed<TSourceData extends StoreDataShape<TSourceData>, TEntity>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, options: {
255
765
  extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
256
766
  }, targetKey?: StoreKey<TAccum>): ConstrainedBuilder<TEnum, TAccum>;
257
- build(): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
767
+ /**
768
+ * Finalize the builder and create an `InjectionToken` (`providedIn: 'root'`).
769
+ * Available only after all enum keys have been defined.
770
+ */
771
+ build(options?: StoreOptions<InferData<TAccum>>): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
258
772
  } : {
773
+ /** Define the next resource slot from the remaining enum keys. */
259
774
  resource<TKey extends Remaining<TEnum, TAccum>>(key: TKey): ConstrainedAsStep<TEnum, TAccum, TKey>;
260
775
  };
776
+ /**
777
+ * Interface-based builder for `Store.for<Config>()`.
778
+ *
779
+ * Uses a config interface for compile-time shape only, so no runtime enum is required.
780
+ */
261
781
  interface InterfaceBuilder<TConfig extends object> {
782
+ /**
783
+ * Mirror a slot from another store. When the source updates, the target is kept in sync.
784
+ *
785
+ * @param source - The source store's `InjectionToken`.
786
+ * @param sourceKey - The key to watch on the source store.
787
+ * @param targetKey - The key on this store to write to. Defaults to `sourceKey`.
788
+ */
262
789
  mirror<TSourceData extends StoreDataShape<TSourceData>>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, targetKey?: StoreKey<ConfigToData<TConfig>>): InterfaceBuilder<TConfig>;
790
+ /**
791
+ * Mirror one slot to another within the same store.
792
+ * Source and target keys must be different.
793
+ *
794
+ * @param sourceKey - The slot to read from.
795
+ * @param targetKey - The slot to write to.
796
+ */
263
797
  mirrorSelf(sourceKey: StoreKey<ConfigToData<TConfig>>, targetKey: StoreKey<ConfigToData<TConfig>>): InterfaceBuilder<TConfig>;
798
+ /**
799
+ * Accumulate single-entity fetches from a source store into a keyed slot.
800
+ *
801
+ * @param source - The source store's `InjectionToken`.
802
+ * @param sourceKey - The single-entity key on the source store.
803
+ * @param options - Must include `extractId` to derive the entity's key from its data.
804
+ * @param targetKey - The keyed slot on this store. Defaults to `sourceKey`.
805
+ */
264
806
  mirrorKeyed<TSourceData extends StoreDataShape<TSourceData>, TEntity>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, options: {
265
807
  extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
266
808
  }, targetKey?: StoreKey<ConfigToData<TConfig>>): InterfaceBuilder<TConfig>;
267
- build(): InjectionToken<IStore<ConfigToData<TConfig>>>;
809
+ /**
810
+ * Finalize the interface-based builder and create a store `InjectionToken`.
811
+ *
812
+ * The resulting token is registered with `providedIn: 'root'` and resolves to
813
+ * a `LazyStore` implementing `IStore<ConfigToData<TConfig>>`.
814
+ *
815
+ * @returns An Angular `InjectionToken` for the configured store.
816
+ *
817
+ * @example
818
+ * ```ts
819
+ * interface ChatStoreConfig {
820
+ * SESSIONS: ChatSession[];
821
+ * MESSAGES: ChatMessage[];
822
+ * }
823
+ *
824
+ * export const ChatStore = Store.for<ChatStoreConfig>().build();
825
+ * ```
826
+ */
827
+ build(options?: StoreOptions<ConfigToData<TConfig>>): InjectionToken<IStore<ConfigToData<TConfig>>>;
268
828
  }
269
829
  interface StoreEntry {
270
830
  /**
@@ -273,6 +833,16 @@ interface StoreEntry {
273
833
  * or call .build() when done.
274
834
  */
275
835
  resource<TKey extends string>(key: TKey): {
836
+ /**
837
+ * Set the resource value type and continue the fluent builder chain.
838
+ *
839
+ * @example
840
+ * ```ts
841
+ * const UsersStore = Store
842
+ * .resource('USERS').as<User[]>()
843
+ * .build();
844
+ * ```
845
+ */
276
846
  as<T>(): StoreBuilder<Record<TKey, ResourceDef<T>>>;
277
847
  };
278
848
  /**
@@ -327,7 +897,7 @@ declare const Store: StoreEntry;
327
897
  *
328
898
  * @example
329
899
  * ```ts
330
- * import { clearAllStores } from 'flurryx';
900
+ * import { clearAllStores } from '@flurryx/store';
331
901
  *
332
902
  * logout() {
333
903
  * clearAllStores();
@@ -398,4 +968,4 @@ interface CollectKeyedOptions<TEntity> {
398
968
  */
399
969
  declare function collectKeyed<TSource extends StoreDataShape<TSource>, TTarget extends StoreDataShape<TTarget>, TEntity = unknown>(source: IStore<TSource>, sourceKey: StoreKey<TSource>, target: IStore<TTarget>, targetKeyOrOptions?: StoreKey<TTarget> | CollectKeyedOptions<TEntity>, options?: CollectKeyedOptions<TEntity>): () => void;
400
970
 
401
- export { BaseStore, type CollectKeyedOptions, type ConfigToData, type IStore, LazyStore, type MirrorOptions, Store, clearAllStores, collectKeyed, mirrorKey };
971
+ export { BaseStore, type BrowserStorageStoreMessageChannelOptions, type ClearAllStoreMessage, type ClearKeyedOneStoreMessage, type ClearStoreMessage, type CollectKeyedOptions, type CompositeStoreMessageChannelOptions, type ConfigToData, type IStore, LazyStore, type MirrorOptions, type StartKeyedLoadingStoreMessage, type StartLoadingStoreMessage, type StopLoadingStoreMessage, type StorageStoreMessageChannelOptions, Store, type StoreDeadLetterEntry, type StoreHistory, type StoreHistoryEntry, type StoreMessage, type StoreMessageChannel, type StoreMessageChannelOptions, type StoreMessageChannelStorage, type StoreMessageRecord, type StoreMessageStatus, type StoreOptions, type StoreSnapshot, type UpdateKeyedOneStoreMessage, type UpdateStoreMessage, clearAllStores, cloneValue, collectKeyed, createCompositeStoreMessageChannel, createInMemoryStoreMessageChannel, createLocalStorageStoreMessageChannel, createSessionStorageStoreMessageChannel, createSnapshotRestorePatch, createStorageStoreMessageChannel, mirrorKey };