@flurryx/store 0.8.3 → 0.8.4
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.cjs +77 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +158 -1
- package/dist/index.d.ts +158 -1
- package/dist/index.js +77 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Signal, InjectionToken } from '@angular/core';
|
|
2
2
|
import { ResourceState, KeyedResourceKey } from '@flurryx/core';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Constraint type ensuring every key in a store data map holds a {@link ResourceState}.
|
|
6
|
+
*/
|
|
4
7
|
type StoreDataShape<TData> = {
|
|
5
8
|
[K in keyof TData]: ResourceState<unknown>;
|
|
6
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the string-typed keys from a store data map.
|
|
12
|
+
*/
|
|
7
13
|
type StoreKey<TData> = keyof TData & string;
|
|
8
14
|
/**
|
|
9
15
|
* Phantom-typed marker for a store resource slot.
|
|
@@ -40,35 +46,124 @@ type ConfigToData<TConfig extends object> = {
|
|
|
40
46
|
};
|
|
41
47
|
/**
|
|
42
48
|
* Shared store interface implemented by both BaseStore and LazyStore.
|
|
49
|
+
*
|
|
50
|
+
* All store instances expose these methods regardless of how they were created
|
|
51
|
+
* (interface-based, fluent, or enum-constrained builder).
|
|
43
52
|
*/
|
|
44
53
|
interface IStore<TData extends StoreDataShape<TData>> {
|
|
54
|
+
/** Returns a read-only `Signal` for the given slot. */
|
|
45
55
|
get<K extends StoreKey<TData>>(key: K): Signal<TData[K]>;
|
|
56
|
+
/** Merges a partial state into the given slot (immutable spread). */
|
|
46
57
|
update<K extends StoreKey<TData>>(key: K, newState: Partial<TData[K]>): void;
|
|
58
|
+
/** Resets a slot to its initial idle state (`{ data: undefined, isLoading: false, … }`). */
|
|
47
59
|
clear<K extends StoreKey<TData>>(key: K): void;
|
|
60
|
+
/** Resets every slot in this store. */
|
|
48
61
|
clearAll(): void;
|
|
62
|
+
/** Marks a slot as loading: sets `isLoading: true` and clears `status`/`errors`. */
|
|
49
63
|
startLoading<K extends StoreKey<TData>>(key: K): void;
|
|
64
|
+
/** Marks a slot as no longer loading: sets `isLoading: false`. */
|
|
50
65
|
stopLoading<K extends StoreKey<TData>>(key: K): void;
|
|
66
|
+
/** Merges a single entity into a keyed slot and sets its status to `'Success'`. */
|
|
51
67
|
updateKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
|
|
68
|
+
/** Removes a single entity (and its loading/status/errors) from a keyed slot. */
|
|
52
69
|
clearKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
|
|
70
|
+
/** Marks a single entity within a keyed slot as loading. */
|
|
53
71
|
startKeyedLoading<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
|
|
72
|
+
/**
|
|
73
|
+
* Registers a callback invoked whenever the given slot is updated.
|
|
74
|
+
* @returns A cleanup function that removes the listener.
|
|
75
|
+
*/
|
|
54
76
|
onUpdate<K extends StoreKey<TData>>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
|
|
55
77
|
}
|
|
56
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Abstract base class for flurryx stores.
|
|
81
|
+
*
|
|
82
|
+
* Backed by Angular `signal()` per slot, providing read-only `Signal` access
|
|
83
|
+
* and immutable updates. All writes go through the store's own methods to
|
|
84
|
+
* enforce single-owner encapsulation.
|
|
85
|
+
*
|
|
86
|
+
* Use the {@link Store} builder to create instances — do not subclass directly.
|
|
87
|
+
*
|
|
88
|
+
* @template TEnum - Record mapping slot names to their string/number keys.
|
|
89
|
+
* @template TData - Record mapping slot names to `ResourceState<T>` types.
|
|
90
|
+
*/
|
|
57
91
|
declare abstract class BaseStore<TEnum extends Record<string, string | number>, TData extends {
|
|
58
92
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
59
93
|
}> implements IStore<TData> {
|
|
60
94
|
protected readonly storeEnum: TEnum;
|
|
61
95
|
private readonly signalsState;
|
|
62
96
|
protected constructor(storeEnum: TEnum);
|
|
97
|
+
/**
|
|
98
|
+
* Returns a **read-only** `Signal` for the given store slot.
|
|
99
|
+
*
|
|
100
|
+
* @param key - The slot name to read.
|
|
101
|
+
* @returns A `Signal` wrapping the slot's current {@link ResourceState}.
|
|
102
|
+
*/
|
|
63
103
|
get<K extends keyof TData>(key: K): Signal<TData[K]>;
|
|
104
|
+
/**
|
|
105
|
+
* Registers a callback fired after every `update` or `clear` on the given slot.
|
|
106
|
+
*
|
|
107
|
+
* @param key - The slot to watch.
|
|
108
|
+
* @param callback - Receives the new state and the previous state.
|
|
109
|
+
* @returns A cleanup function that removes the listener when called.
|
|
110
|
+
*/
|
|
64
111
|
onUpdate<K extends keyof TData>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
|
|
112
|
+
/**
|
|
113
|
+
* Partially updates a slot by merging `newState` into the current value (immutable spread).
|
|
114
|
+
*
|
|
115
|
+
* @param key - The slot to update.
|
|
116
|
+
* @param newState - Partial state to merge (e.g. `{ data: newData, status: 'Success' }`).
|
|
117
|
+
*/
|
|
65
118
|
update<K extends keyof TData>(key: K, newState: Partial<TData[K]>): void;
|
|
119
|
+
/** Resets every slot in this store to its initial idle state. */
|
|
66
120
|
clearAll(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Resets a single slot to `{ data: undefined, isLoading: false, status: undefined, errors: undefined }`.
|
|
123
|
+
*
|
|
124
|
+
* @param key - The slot to clear.
|
|
125
|
+
*/
|
|
67
126
|
clear<K extends keyof TData>(key: K): void;
|
|
127
|
+
/**
|
|
128
|
+
* Marks a slot as loading: sets `isLoading: true` and clears `status` and `errors`.
|
|
129
|
+
*
|
|
130
|
+
* @param key - The slot to mark as loading.
|
|
131
|
+
*/
|
|
68
132
|
startLoading<K extends keyof TData>(key: K): void;
|
|
133
|
+
/**
|
|
134
|
+
* Marks a slot as no longer loading: sets `isLoading: false`.
|
|
135
|
+
* Does **not** clear `status` or `errors`.
|
|
136
|
+
*
|
|
137
|
+
* @param key - The slot to stop loading.
|
|
138
|
+
*/
|
|
69
139
|
stopLoading<K extends keyof TData>(key: K): void;
|
|
140
|
+
/**
|
|
141
|
+
* Merges a single entity into a {@link KeyedResourceData} slot.
|
|
142
|
+
* Sets its status to `'Success'` and clears per-key errors.
|
|
143
|
+
* The top-level `isLoading` is recalculated based on remaining loading keys.
|
|
144
|
+
*
|
|
145
|
+
* @param key - The keyed slot name.
|
|
146
|
+
* @param resourceKey - The entity identifier (e.g. `'inv-123'`).
|
|
147
|
+
* @param entity - The entity value to store.
|
|
148
|
+
*/
|
|
70
149
|
updateKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
|
|
150
|
+
/**
|
|
151
|
+
* Removes a single entity from a {@link KeyedResourceData} slot,
|
|
152
|
+
* including its loading flag, status, and errors.
|
|
153
|
+
* Recalculates the top-level `isLoading` from the remaining keys.
|
|
154
|
+
*
|
|
155
|
+
* @param key - The keyed slot name.
|
|
156
|
+
* @param resourceKey - The entity identifier to remove.
|
|
157
|
+
*/
|
|
71
158
|
clearKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
|
|
159
|
+
/**
|
|
160
|
+
* Marks a single entity within a keyed slot as loading.
|
|
161
|
+
* Clears its status and errors. If the slot data is not yet a {@link KeyedResourceData},
|
|
162
|
+
* falls back to `startLoading(key)`.
|
|
163
|
+
*
|
|
164
|
+
* @param key - The keyed slot name.
|
|
165
|
+
* @param resourceKey - The entity identifier to mark as loading.
|
|
166
|
+
*/
|
|
72
167
|
startKeyedLoading<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
|
|
73
168
|
private notifyUpdateHooks;
|
|
74
169
|
private initializeState;
|
|
@@ -105,15 +200,42 @@ interface AsStep<TAccum extends StoreConfig, TKey extends string> {
|
|
|
105
200
|
}
|
|
106
201
|
/**
|
|
107
202
|
* Fluent builder for creating stores.
|
|
108
|
-
* Accumulates resource definitions then produces an InjectionToken on
|
|
203
|
+
* Accumulates resource definitions then produces an `InjectionToken` on `.build()`.
|
|
109
204
|
*/
|
|
110
205
|
interface StoreBuilder<TAccum extends StoreConfig> {
|
|
206
|
+
/** Define a new resource slot. Chain `.as<T>()` to set its type. */
|
|
111
207
|
resource<TKey extends string>(key: TKey): AsStep<TAccum, TKey>;
|
|
208
|
+
/**
|
|
209
|
+
* Mirror a slot from another store. When the source updates, the target is kept in sync.
|
|
210
|
+
*
|
|
211
|
+
* @param source - The source store's `InjectionToken`.
|
|
212
|
+
* @param sourceKey - The key to watch on the source store.
|
|
213
|
+
* @param targetKey - The key on this store to write to. Defaults to `sourceKey`.
|
|
214
|
+
*/
|
|
112
215
|
mirror<TSourceData extends StoreDataShape<TSourceData>>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, targetKey?: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
216
|
+
/**
|
|
217
|
+
* Mirror one slot to another **within the same store**.
|
|
218
|
+
* Source and target keys must be different.
|
|
219
|
+
*
|
|
220
|
+
* @param sourceKey - The slot to read from.
|
|
221
|
+
* @param targetKey - The slot to write to.
|
|
222
|
+
*/
|
|
113
223
|
mirrorSelf(sourceKey: StoreKey<TAccum>, targetKey: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
224
|
+
/**
|
|
225
|
+
* Accumulate single-entity fetches from a source store into a `KeyedResourceData` slot.
|
|
226
|
+
*
|
|
227
|
+
* @param source - The source store's `InjectionToken`.
|
|
228
|
+
* @param sourceKey - The single-entity key on the source store.
|
|
229
|
+
* @param options - Must include `extractId` to derive the entity's key from its data.
|
|
230
|
+
* @param targetKey - The keyed slot on this store. Defaults to `sourceKey`.
|
|
231
|
+
*/
|
|
114
232
|
mirrorKeyed<TSourceData extends StoreDataShape<TSourceData>, TEntity>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, options: {
|
|
115
233
|
extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
|
|
116
234
|
}, targetKey?: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
235
|
+
/**
|
|
236
|
+
* Finalize the builder and create an `InjectionToken` (`providedIn: 'root'`).
|
|
237
|
+
* All mirrors are wired up automatically when Angular creates the store.
|
|
238
|
+
*/
|
|
117
239
|
build(): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
|
|
118
240
|
}
|
|
119
241
|
/** Keys from the enum that have NOT yet been defined. */
|
|
@@ -197,9 +319,31 @@ interface StoreEntry {
|
|
|
197
319
|
*/
|
|
198
320
|
declare const Store: StoreEntry;
|
|
199
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Clears every store instance tracked by flurryx.
|
|
324
|
+
*
|
|
325
|
+
* Calls `clearAll()` on each registered store, resetting all slots to their
|
|
326
|
+
* initial idle state. Useful for logout, tenant switching, or test cleanup.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* import { clearAllStores } from 'flurryx';
|
|
331
|
+
*
|
|
332
|
+
* logout() {
|
|
333
|
+
* clearAllStores();
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
200
337
|
declare function clearAllStores(): void;
|
|
201
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Options for {@link mirrorKey}.
|
|
341
|
+
*/
|
|
202
342
|
interface MirrorOptions {
|
|
343
|
+
/**
|
|
344
|
+
* Angular `DestroyRef` (or any object with an `onDestroy` method) for
|
|
345
|
+
* automatic cleanup. When provided, the mirror stops when the ref is destroyed.
|
|
346
|
+
*/
|
|
203
347
|
destroyRef?: {
|
|
204
348
|
onDestroy: (fn: () => void) => void;
|
|
205
349
|
};
|
|
@@ -217,8 +361,21 @@ interface MirrorOptions {
|
|
|
217
361
|
*/
|
|
218
362
|
declare function mirrorKey<TSource extends StoreDataShape<TSource>, TTarget extends StoreDataShape<TTarget>>(source: IStore<TSource>, sourceKey: StoreKey<TSource>, target: IStore<TTarget>, targetKeyOrOptions?: StoreKey<TTarget> | MirrorOptions, options?: MirrorOptions): () => void;
|
|
219
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Options for {@link collectKeyed}.
|
|
366
|
+
*
|
|
367
|
+
* @template TEntity - The entity type emitted by the source store.
|
|
368
|
+
*/
|
|
220
369
|
interface CollectKeyedOptions<TEntity> {
|
|
370
|
+
/**
|
|
371
|
+
* Extracts the entity identifier from the source data.
|
|
372
|
+
* Return `undefined` to skip accumulation for that emission.
|
|
373
|
+
*/
|
|
221
374
|
extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
|
|
375
|
+
/**
|
|
376
|
+
* Angular `DestroyRef` (or any object with an `onDestroy` method) for
|
|
377
|
+
* automatic cleanup. When provided, collection stops when the ref is destroyed.
|
|
378
|
+
*/
|
|
222
379
|
destroyRef?: {
|
|
223
380
|
onDestroy: (fn: () => void) => void;
|
|
224
381
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Signal, InjectionToken } from '@angular/core';
|
|
2
2
|
import { ResourceState, KeyedResourceKey } from '@flurryx/core';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* Constraint type ensuring every key in a store data map holds a {@link ResourceState}.
|
|
6
|
+
*/
|
|
4
7
|
type StoreDataShape<TData> = {
|
|
5
8
|
[K in keyof TData]: ResourceState<unknown>;
|
|
6
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Extracts the string-typed keys from a store data map.
|
|
12
|
+
*/
|
|
7
13
|
type StoreKey<TData> = keyof TData & string;
|
|
8
14
|
/**
|
|
9
15
|
* Phantom-typed marker for a store resource slot.
|
|
@@ -40,35 +46,124 @@ type ConfigToData<TConfig extends object> = {
|
|
|
40
46
|
};
|
|
41
47
|
/**
|
|
42
48
|
* Shared store interface implemented by both BaseStore and LazyStore.
|
|
49
|
+
*
|
|
50
|
+
* All store instances expose these methods regardless of how they were created
|
|
51
|
+
* (interface-based, fluent, or enum-constrained builder).
|
|
43
52
|
*/
|
|
44
53
|
interface IStore<TData extends StoreDataShape<TData>> {
|
|
54
|
+
/** Returns a read-only `Signal` for the given slot. */
|
|
45
55
|
get<K extends StoreKey<TData>>(key: K): Signal<TData[K]>;
|
|
56
|
+
/** Merges a partial state into the given slot (immutable spread). */
|
|
46
57
|
update<K extends StoreKey<TData>>(key: K, newState: Partial<TData[K]>): void;
|
|
58
|
+
/** Resets a slot to its initial idle state (`{ data: undefined, isLoading: false, … }`). */
|
|
47
59
|
clear<K extends StoreKey<TData>>(key: K): void;
|
|
60
|
+
/** Resets every slot in this store. */
|
|
48
61
|
clearAll(): void;
|
|
62
|
+
/** Marks a slot as loading: sets `isLoading: true` and clears `status`/`errors`. */
|
|
49
63
|
startLoading<K extends StoreKey<TData>>(key: K): void;
|
|
64
|
+
/** Marks a slot as no longer loading: sets `isLoading: false`. */
|
|
50
65
|
stopLoading<K extends StoreKey<TData>>(key: K): void;
|
|
66
|
+
/** Merges a single entity into a keyed slot and sets its status to `'Success'`. */
|
|
51
67
|
updateKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
|
|
68
|
+
/** Removes a single entity (and its loading/status/errors) from a keyed slot. */
|
|
52
69
|
clearKeyedOne<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
|
|
70
|
+
/** Marks a single entity within a keyed slot as loading. */
|
|
53
71
|
startKeyedLoading<K extends StoreKey<TData>>(key: K, resourceKey: KeyedResourceKey): void;
|
|
72
|
+
/**
|
|
73
|
+
* Registers a callback invoked whenever the given slot is updated.
|
|
74
|
+
* @returns A cleanup function that removes the listener.
|
|
75
|
+
*/
|
|
54
76
|
onUpdate<K extends StoreKey<TData>>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
|
|
55
77
|
}
|
|
56
78
|
|
|
79
|
+
/**
|
|
80
|
+
* Abstract base class for flurryx stores.
|
|
81
|
+
*
|
|
82
|
+
* Backed by Angular `signal()` per slot, providing read-only `Signal` access
|
|
83
|
+
* and immutable updates. All writes go through the store's own methods to
|
|
84
|
+
* enforce single-owner encapsulation.
|
|
85
|
+
*
|
|
86
|
+
* Use the {@link Store} builder to create instances — do not subclass directly.
|
|
87
|
+
*
|
|
88
|
+
* @template TEnum - Record mapping slot names to their string/number keys.
|
|
89
|
+
* @template TData - Record mapping slot names to `ResourceState<T>` types.
|
|
90
|
+
*/
|
|
57
91
|
declare abstract class BaseStore<TEnum extends Record<string, string | number>, TData extends {
|
|
58
92
|
[K in keyof TEnum]: ResourceState<unknown>;
|
|
59
93
|
}> implements IStore<TData> {
|
|
60
94
|
protected readonly storeEnum: TEnum;
|
|
61
95
|
private readonly signalsState;
|
|
62
96
|
protected constructor(storeEnum: TEnum);
|
|
97
|
+
/**
|
|
98
|
+
* Returns a **read-only** `Signal` for the given store slot.
|
|
99
|
+
*
|
|
100
|
+
* @param key - The slot name to read.
|
|
101
|
+
* @returns A `Signal` wrapping the slot's current {@link ResourceState}.
|
|
102
|
+
*/
|
|
63
103
|
get<K extends keyof TData>(key: K): Signal<TData[K]>;
|
|
104
|
+
/**
|
|
105
|
+
* Registers a callback fired after every `update` or `clear` on the given slot.
|
|
106
|
+
*
|
|
107
|
+
* @param key - The slot to watch.
|
|
108
|
+
* @param callback - Receives the new state and the previous state.
|
|
109
|
+
* @returns A cleanup function that removes the listener when called.
|
|
110
|
+
*/
|
|
64
111
|
onUpdate<K extends keyof TData>(key: K, callback: (state: TData[K], previousState: TData[K]) => void): () => void;
|
|
112
|
+
/**
|
|
113
|
+
* Partially updates a slot by merging `newState` into the current value (immutable spread).
|
|
114
|
+
*
|
|
115
|
+
* @param key - The slot to update.
|
|
116
|
+
* @param newState - Partial state to merge (e.g. `{ data: newData, status: 'Success' }`).
|
|
117
|
+
*/
|
|
65
118
|
update<K extends keyof TData>(key: K, newState: Partial<TData[K]>): void;
|
|
119
|
+
/** Resets every slot in this store to its initial idle state. */
|
|
66
120
|
clearAll(): void;
|
|
121
|
+
/**
|
|
122
|
+
* Resets a single slot to `{ data: undefined, isLoading: false, status: undefined, errors: undefined }`.
|
|
123
|
+
*
|
|
124
|
+
* @param key - The slot to clear.
|
|
125
|
+
*/
|
|
67
126
|
clear<K extends keyof TData>(key: K): void;
|
|
127
|
+
/**
|
|
128
|
+
* Marks a slot as loading: sets `isLoading: true` and clears `status` and `errors`.
|
|
129
|
+
*
|
|
130
|
+
* @param key - The slot to mark as loading.
|
|
131
|
+
*/
|
|
68
132
|
startLoading<K extends keyof TData>(key: K): void;
|
|
133
|
+
/**
|
|
134
|
+
* Marks a slot as no longer loading: sets `isLoading: false`.
|
|
135
|
+
* Does **not** clear `status` or `errors`.
|
|
136
|
+
*
|
|
137
|
+
* @param key - The slot to stop loading.
|
|
138
|
+
*/
|
|
69
139
|
stopLoading<K extends keyof TData>(key: K): void;
|
|
140
|
+
/**
|
|
141
|
+
* Merges a single entity into a {@link KeyedResourceData} slot.
|
|
142
|
+
* Sets its status to `'Success'` and clears per-key errors.
|
|
143
|
+
* The top-level `isLoading` is recalculated based on remaining loading keys.
|
|
144
|
+
*
|
|
145
|
+
* @param key - The keyed slot name.
|
|
146
|
+
* @param resourceKey - The entity identifier (e.g. `'inv-123'`).
|
|
147
|
+
* @param entity - The entity value to store.
|
|
148
|
+
*/
|
|
70
149
|
updateKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey, entity: unknown): void;
|
|
150
|
+
/**
|
|
151
|
+
* Removes a single entity from a {@link KeyedResourceData} slot,
|
|
152
|
+
* including its loading flag, status, and errors.
|
|
153
|
+
* Recalculates the top-level `isLoading` from the remaining keys.
|
|
154
|
+
*
|
|
155
|
+
* @param key - The keyed slot name.
|
|
156
|
+
* @param resourceKey - The entity identifier to remove.
|
|
157
|
+
*/
|
|
71
158
|
clearKeyedOne<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
|
|
159
|
+
/**
|
|
160
|
+
* Marks a single entity within a keyed slot as loading.
|
|
161
|
+
* Clears its status and errors. If the slot data is not yet a {@link KeyedResourceData},
|
|
162
|
+
* falls back to `startLoading(key)`.
|
|
163
|
+
*
|
|
164
|
+
* @param key - The keyed slot name.
|
|
165
|
+
* @param resourceKey - The entity identifier to mark as loading.
|
|
166
|
+
*/
|
|
72
167
|
startKeyedLoading<K extends keyof TData>(key: K, resourceKey: KeyedResourceKey): void;
|
|
73
168
|
private notifyUpdateHooks;
|
|
74
169
|
private initializeState;
|
|
@@ -105,15 +200,42 @@ interface AsStep<TAccum extends StoreConfig, TKey extends string> {
|
|
|
105
200
|
}
|
|
106
201
|
/**
|
|
107
202
|
* Fluent builder for creating stores.
|
|
108
|
-
* Accumulates resource definitions then produces an InjectionToken on
|
|
203
|
+
* Accumulates resource definitions then produces an `InjectionToken` on `.build()`.
|
|
109
204
|
*/
|
|
110
205
|
interface StoreBuilder<TAccum extends StoreConfig> {
|
|
206
|
+
/** Define a new resource slot. Chain `.as<T>()` to set its type. */
|
|
111
207
|
resource<TKey extends string>(key: TKey): AsStep<TAccum, TKey>;
|
|
208
|
+
/**
|
|
209
|
+
* Mirror a slot from another store. When the source updates, the target is kept in sync.
|
|
210
|
+
*
|
|
211
|
+
* @param source - The source store's `InjectionToken`.
|
|
212
|
+
* @param sourceKey - The key to watch on the source store.
|
|
213
|
+
* @param targetKey - The key on this store to write to. Defaults to `sourceKey`.
|
|
214
|
+
*/
|
|
112
215
|
mirror<TSourceData extends StoreDataShape<TSourceData>>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, targetKey?: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
216
|
+
/**
|
|
217
|
+
* Mirror one slot to another **within the same store**.
|
|
218
|
+
* Source and target keys must be different.
|
|
219
|
+
*
|
|
220
|
+
* @param sourceKey - The slot to read from.
|
|
221
|
+
* @param targetKey - The slot to write to.
|
|
222
|
+
*/
|
|
113
223
|
mirrorSelf(sourceKey: StoreKey<TAccum>, targetKey: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
224
|
+
/**
|
|
225
|
+
* Accumulate single-entity fetches from a source store into a `KeyedResourceData` slot.
|
|
226
|
+
*
|
|
227
|
+
* @param source - The source store's `InjectionToken`.
|
|
228
|
+
* @param sourceKey - The single-entity key on the source store.
|
|
229
|
+
* @param options - Must include `extractId` to derive the entity's key from its data.
|
|
230
|
+
* @param targetKey - The keyed slot on this store. Defaults to `sourceKey`.
|
|
231
|
+
*/
|
|
114
232
|
mirrorKeyed<TSourceData extends StoreDataShape<TSourceData>, TEntity>(source: InjectionToken<IStore<TSourceData>>, sourceKey: StoreKey<TSourceData>, options: {
|
|
115
233
|
extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
|
|
116
234
|
}, targetKey?: StoreKey<TAccum>): StoreBuilder<TAccum>;
|
|
235
|
+
/**
|
|
236
|
+
* Finalize the builder and create an `InjectionToken` (`providedIn: 'root'`).
|
|
237
|
+
* All mirrors are wired up automatically when Angular creates the store.
|
|
238
|
+
*/
|
|
117
239
|
build(): InjectionToken<BaseStore<InferEnum<TAccum>, InferData<TAccum>>>;
|
|
118
240
|
}
|
|
119
241
|
/** Keys from the enum that have NOT yet been defined. */
|
|
@@ -197,9 +319,31 @@ interface StoreEntry {
|
|
|
197
319
|
*/
|
|
198
320
|
declare const Store: StoreEntry;
|
|
199
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Clears every store instance tracked by flurryx.
|
|
324
|
+
*
|
|
325
|
+
* Calls `clearAll()` on each registered store, resetting all slots to their
|
|
326
|
+
* initial idle state. Useful for logout, tenant switching, or test cleanup.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* ```ts
|
|
330
|
+
* import { clearAllStores } from 'flurryx';
|
|
331
|
+
*
|
|
332
|
+
* logout() {
|
|
333
|
+
* clearAllStores();
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
200
337
|
declare function clearAllStores(): void;
|
|
201
338
|
|
|
339
|
+
/**
|
|
340
|
+
* Options for {@link mirrorKey}.
|
|
341
|
+
*/
|
|
202
342
|
interface MirrorOptions {
|
|
343
|
+
/**
|
|
344
|
+
* Angular `DestroyRef` (or any object with an `onDestroy` method) for
|
|
345
|
+
* automatic cleanup. When provided, the mirror stops when the ref is destroyed.
|
|
346
|
+
*/
|
|
203
347
|
destroyRef?: {
|
|
204
348
|
onDestroy: (fn: () => void) => void;
|
|
205
349
|
};
|
|
@@ -217,8 +361,21 @@ interface MirrorOptions {
|
|
|
217
361
|
*/
|
|
218
362
|
declare function mirrorKey<TSource extends StoreDataShape<TSource>, TTarget extends StoreDataShape<TTarget>>(source: IStore<TSource>, sourceKey: StoreKey<TSource>, target: IStore<TTarget>, targetKeyOrOptions?: StoreKey<TTarget> | MirrorOptions, options?: MirrorOptions): () => void;
|
|
219
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Options for {@link collectKeyed}.
|
|
366
|
+
*
|
|
367
|
+
* @template TEntity - The entity type emitted by the source store.
|
|
368
|
+
*/
|
|
220
369
|
interface CollectKeyedOptions<TEntity> {
|
|
370
|
+
/**
|
|
371
|
+
* Extracts the entity identifier from the source data.
|
|
372
|
+
* Return `undefined` to skip accumulation for that emission.
|
|
373
|
+
*/
|
|
221
374
|
extractId: (data: TEntity | undefined) => KeyedResourceKey | undefined;
|
|
375
|
+
/**
|
|
376
|
+
* Angular `DestroyRef` (or any object with an `onDestroy` method) for
|
|
377
|
+
* automatic cleanup. When provided, collection stops when the ref is destroyed.
|
|
378
|
+
*/
|
|
222
379
|
destroyRef?: {
|
|
223
380
|
onDestroy: (fn: () => void) => void;
|
|
224
381
|
};
|
package/dist/index.js
CHANGED
|
@@ -27,9 +27,22 @@ var BaseStore = class {
|
|
|
27
27
|
trackStore(this);
|
|
28
28
|
}
|
|
29
29
|
signalsState = /* @__PURE__ */ new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Returns a **read-only** `Signal` for the given store slot.
|
|
32
|
+
*
|
|
33
|
+
* @param key - The slot name to read.
|
|
34
|
+
* @returns A `Signal` wrapping the slot's current {@link ResourceState}.
|
|
35
|
+
*/
|
|
30
36
|
get(key) {
|
|
31
37
|
return this.signalsState.get(key.toString());
|
|
32
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Registers a callback fired after every `update` or `clear` on the given slot.
|
|
41
|
+
*
|
|
42
|
+
* @param key - The slot to watch.
|
|
43
|
+
* @param callback - Receives the new state and the previous state.
|
|
44
|
+
* @returns A cleanup function that removes the listener when called.
|
|
45
|
+
*/
|
|
33
46
|
onUpdate(key, callback) {
|
|
34
47
|
const hooks = updateHooksMap.get(this);
|
|
35
48
|
if (!hooks.has(key)) {
|
|
@@ -51,6 +64,12 @@ var BaseStore = class {
|
|
|
51
64
|
}
|
|
52
65
|
};
|
|
53
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Partially updates a slot by merging `newState` into the current value (immutable spread).
|
|
69
|
+
*
|
|
70
|
+
* @param key - The slot to update.
|
|
71
|
+
* @param newState - Partial state to merge (e.g. `{ data: newData, status: 'Success' }`).
|
|
72
|
+
*/
|
|
54
73
|
update(key, newState) {
|
|
55
74
|
const currentState = this.signalsState.get(key.toString());
|
|
56
75
|
if (!currentState) {
|
|
@@ -64,11 +83,17 @@ var BaseStore = class {
|
|
|
64
83
|
const updatedState = currentState();
|
|
65
84
|
this.notifyUpdateHooks(key, updatedState, previousState);
|
|
66
85
|
}
|
|
86
|
+
/** Resets every slot in this store to its initial idle state. */
|
|
67
87
|
clearAll() {
|
|
68
88
|
Object.keys(this.storeEnum).forEach((key) => {
|
|
69
89
|
this.clear(key);
|
|
70
90
|
});
|
|
71
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Resets a single slot to `{ data: undefined, isLoading: false, status: undefined, errors: undefined }`.
|
|
94
|
+
*
|
|
95
|
+
* @param key - The slot to clear.
|
|
96
|
+
*/
|
|
72
97
|
clear(key) {
|
|
73
98
|
const currentState = this.signalsState.get(key.toString());
|
|
74
99
|
if (!currentState) {
|
|
@@ -85,6 +110,11 @@ var BaseStore = class {
|
|
|
85
110
|
const nextState = currentState();
|
|
86
111
|
this.notifyUpdateHooks(key, nextState, previousState);
|
|
87
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Marks a slot as loading: sets `isLoading: true` and clears `status` and `errors`.
|
|
115
|
+
*
|
|
116
|
+
* @param key - The slot to mark as loading.
|
|
117
|
+
*/
|
|
88
118
|
startLoading(key) {
|
|
89
119
|
const currentState = this.signalsState.get(key.toString());
|
|
90
120
|
if (!currentState) {
|
|
@@ -100,6 +130,12 @@ var BaseStore = class {
|
|
|
100
130
|
})
|
|
101
131
|
);
|
|
102
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Marks a slot as no longer loading: sets `isLoading: false`.
|
|
135
|
+
* Does **not** clear `status` or `errors`.
|
|
136
|
+
*
|
|
137
|
+
* @param key - The slot to stop loading.
|
|
138
|
+
*/
|
|
103
139
|
stopLoading(key) {
|
|
104
140
|
const currentState = this.signalsState.get(key.toString());
|
|
105
141
|
if (!currentState) {
|
|
@@ -113,6 +149,15 @@ var BaseStore = class {
|
|
|
113
149
|
})
|
|
114
150
|
);
|
|
115
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Merges a single entity into a {@link KeyedResourceData} slot.
|
|
154
|
+
* Sets its status to `'Success'` and clears per-key errors.
|
|
155
|
+
* The top-level `isLoading` is recalculated based on remaining loading keys.
|
|
156
|
+
*
|
|
157
|
+
* @param key - The keyed slot name.
|
|
158
|
+
* @param resourceKey - The entity identifier (e.g. `'inv-123'`).
|
|
159
|
+
* @param entity - The entity value to store.
|
|
160
|
+
*/
|
|
116
161
|
updateKeyedOne(key, resourceKey, entity) {
|
|
117
162
|
const currentState = this.signalsState.get(key.toString());
|
|
118
163
|
if (!currentState) {
|
|
@@ -145,6 +190,14 @@ var BaseStore = class {
|
|
|
145
190
|
errors: void 0
|
|
146
191
|
});
|
|
147
192
|
}
|
|
193
|
+
/**
|
|
194
|
+
* Removes a single entity from a {@link KeyedResourceData} slot,
|
|
195
|
+
* including its loading flag, status, and errors.
|
|
196
|
+
* Recalculates the top-level `isLoading` from the remaining keys.
|
|
197
|
+
*
|
|
198
|
+
* @param key - The keyed slot name.
|
|
199
|
+
* @param resourceKey - The entity identifier to remove.
|
|
200
|
+
*/
|
|
148
201
|
clearKeyedOne(key, resourceKey) {
|
|
149
202
|
const currentState = this.signalsState.get(key.toString());
|
|
150
203
|
if (!currentState) {
|
|
@@ -184,6 +237,14 @@ var BaseStore = class {
|
|
|
184
237
|
const updatedState = currentState();
|
|
185
238
|
this.notifyUpdateHooks(key, updatedState, previousState);
|
|
186
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Marks a single entity within a keyed slot as loading.
|
|
242
|
+
* Clears its status and errors. If the slot data is not yet a {@link KeyedResourceData},
|
|
243
|
+
* falls back to `startLoading(key)`.
|
|
244
|
+
*
|
|
245
|
+
* @param key - The keyed slot name.
|
|
246
|
+
* @param resourceKey - The entity identifier to mark as loading.
|
|
247
|
+
*/
|
|
187
248
|
startKeyedLoading(key, resourceKey) {
|
|
188
249
|
const currentState = this.signalsState.get(key.toString());
|
|
189
250
|
if (!currentState) {
|
|
@@ -497,9 +558,15 @@ function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
497
558
|
return;
|
|
498
559
|
}
|
|
499
560
|
if (resourceState.status === "Success" && currentId !== void 0) {
|
|
500
|
-
const newEntities = {
|
|
561
|
+
const newEntities = {
|
|
562
|
+
...currentKeyed.entities,
|
|
563
|
+
[currentId]: resourceState.data
|
|
564
|
+
};
|
|
501
565
|
const newIsLoading = { ...currentKeyed.isLoading, [currentId]: false };
|
|
502
|
-
const newStatus = {
|
|
566
|
+
const newStatus = {
|
|
567
|
+
...currentKeyed.status,
|
|
568
|
+
[currentId]: resourceState.status
|
|
569
|
+
};
|
|
503
570
|
const newErrors = { ...currentKeyed.errors };
|
|
504
571
|
delete newErrors[currentId];
|
|
505
572
|
const updatedKeyed = {
|
|
@@ -516,8 +583,14 @@ function collectKeyed(source, sourceKey, target, targetKeyOrOptions, options) {
|
|
|
516
583
|
previousId = currentId;
|
|
517
584
|
} else if (resourceState.status === "Error" && currentId !== void 0) {
|
|
518
585
|
const newIsLoading = { ...currentKeyed.isLoading, [currentId]: false };
|
|
519
|
-
const newStatus = {
|
|
520
|
-
|
|
586
|
+
const newStatus = {
|
|
587
|
+
...currentKeyed.status,
|
|
588
|
+
[currentId]: resourceState.status
|
|
589
|
+
};
|
|
590
|
+
const newErrors = {
|
|
591
|
+
...currentKeyed.errors,
|
|
592
|
+
[currentId]: resourceState.errors
|
|
593
|
+
};
|
|
521
594
|
const updatedKeyed = {
|
|
522
595
|
entities: { ...currentKeyed.entities },
|
|
523
596
|
isLoading: newIsLoading,
|