@angular-architects/ngrx-toolkit 19.4.0 → 19.4.2

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 (128) hide show
  1. package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +119 -0
  2. package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +1 -0
  3. package/fesm2022/angular-architects-ngrx-toolkit.mjs +2108 -0
  4. package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
  5. package/{src/index.ts → index.d.ts} +5 -32
  6. package/lib/assertions/assertions.d.ts +2 -0
  7. package/{src/lib/devtools/features/with-disabled-name-indicies.ts → lib/devtools/features/with-disabled-name-indicies.d.ts} +1 -5
  8. package/{src/lib/devtools/features/with-glitch-tracking.ts → lib/devtools/features/with-glitch-tracking.d.ts} +1 -6
  9. package/{src/lib/devtools/features/with-mapper.ts → lib/devtools/features/with-mapper.d.ts} +1 -7
  10. package/lib/devtools/internal/current-action-names.d.ts +1 -0
  11. package/lib/devtools/internal/default-tracker.d.ts +13 -0
  12. package/lib/devtools/internal/devtools-feature.d.ts +24 -0
  13. package/lib/devtools/internal/devtools-syncer.service.d.ts +39 -0
  14. package/lib/devtools/internal/glitch-tracker.service.d.ts +18 -0
  15. package/lib/devtools/internal/models.d.ts +24 -0
  16. package/{src/lib/devtools/provide-devtools-config.ts → lib/devtools/provide-devtools-config.d.ts} +4 -16
  17. package/lib/devtools/rename-devtools-name.d.ts +7 -0
  18. package/lib/devtools/update-state.d.ts +15 -0
  19. package/{src/lib/devtools/with-dev-tools-stub.ts → lib/devtools/with-dev-tools-stub.d.ts} +1 -2
  20. package/lib/devtools/with-devtools.d.ts +24 -0
  21. package/lib/flattening-operator.d.ts +14 -0
  22. package/lib/immutable-state/deep-freeze.d.ts +11 -0
  23. package/lib/immutable-state/is-dev-mode.d.ts +1 -0
  24. package/lib/immutable-state/with-immutable-state.d.ts +60 -0
  25. package/lib/mutation/http-mutation.d.ts +86 -0
  26. package/lib/mutation/mutation.d.ts +20 -0
  27. package/lib/mutation/rx-mutation.d.ts +76 -0
  28. package/{src/lib/shared/signal-store-models.ts → lib/shared/signal-store-models.d.ts} +4 -8
  29. package/lib/shared/throw-if-null.d.ts +1 -0
  30. package/lib/storage-sync/features/with-indexed-db.d.ts +2 -0
  31. package/lib/storage-sync/features/with-local-storage.d.ts +3 -0
  32. package/lib/storage-sync/internal/indexeddb.service.d.ts +29 -0
  33. package/lib/storage-sync/internal/local-storage.service.d.ts +8 -0
  34. package/lib/storage-sync/internal/models.d.ts +45 -0
  35. package/lib/storage-sync/internal/session-storage.service.d.ts +8 -0
  36. package/lib/storage-sync/with-storage-sync.d.ts +45 -0
  37. package/lib/with-call-state.d.ts +58 -0
  38. package/{src/lib/with-conditional.ts → lib/with-conditional.d.ts} +7 -31
  39. package/lib/with-data-service.d.ts +109 -0
  40. package/{src/lib/with-feature-factory.ts → lib/with-feature-factory.d.ts} +4 -32
  41. package/lib/with-mutations.d.ts +51 -0
  42. package/lib/with-pagination.d.ts +98 -0
  43. package/lib/with-redux.d.ts +147 -0
  44. package/lib/with-reset.d.ts +29 -0
  45. package/lib/with-undo-redo.d.ts +31 -0
  46. package/package.json +21 -4
  47. package/redux-connector/index.d.ts +2 -0
  48. package/redux-connector/src/lib/create-redux.d.ts +13 -0
  49. package/redux-connector/src/lib/model.d.ts +40 -0
  50. package/redux-connector/src/lib/rxjs-interop/redux-method.d.ts +14 -0
  51. package/redux-connector/src/lib/signal-redux-store.d.ts +11 -0
  52. package/redux-connector/src/lib/util.d.ts +5 -0
  53. package/eslint.config.cjs +0 -43
  54. package/jest.config.ts +0 -22
  55. package/ng-package.json +0 -7
  56. package/project.json +0 -37
  57. package/redux-connector/docs/README.md +0 -131
  58. package/redux-connector/index.ts +0 -6
  59. package/redux-connector/ng-package.json +0 -5
  60. package/redux-connector/src/lib/create-redux.ts +0 -102
  61. package/redux-connector/src/lib/model.ts +0 -89
  62. package/redux-connector/src/lib/rxjs-interop/redux-method.ts +0 -66
  63. package/redux-connector/src/lib/signal-redux-store.ts +0 -59
  64. package/redux-connector/src/lib/util.ts +0 -22
  65. package/src/lib/assertions/assertions.ts +0 -9
  66. package/src/lib/devtools/internal/current-action-names.ts +0 -1
  67. package/src/lib/devtools/internal/default-tracker.ts +0 -60
  68. package/src/lib/devtools/internal/devtools-feature.ts +0 -37
  69. package/src/lib/devtools/internal/devtools-syncer.service.ts +0 -202
  70. package/src/lib/devtools/internal/glitch-tracker.service.ts +0 -61
  71. package/src/lib/devtools/internal/models.ts +0 -29
  72. package/src/lib/devtools/rename-devtools-name.ts +0 -21
  73. package/src/lib/devtools/tests/action-name.spec.ts +0 -48
  74. package/src/lib/devtools/tests/basic.spec.ts +0 -111
  75. package/src/lib/devtools/tests/connecting.spec.ts +0 -37
  76. package/src/lib/devtools/tests/helpers.spec.ts +0 -43
  77. package/src/lib/devtools/tests/naming.spec.ts +0 -216
  78. package/src/lib/devtools/tests/provide-devtools-config.spec.ts +0 -25
  79. package/src/lib/devtools/tests/types.spec.ts +0 -19
  80. package/src/lib/devtools/tests/update-state.spec.ts +0 -29
  81. package/src/lib/devtools/tests/with-devtools.spec.ts +0 -5
  82. package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +0 -272
  83. package/src/lib/devtools/tests/with-mapper.spec.ts +0 -69
  84. package/src/lib/devtools/update-state.ts +0 -38
  85. package/src/lib/devtools/with-devtools.ts +0 -81
  86. package/src/lib/flattening-operator.ts +0 -42
  87. package/src/lib/immutable-state/deep-freeze.ts +0 -43
  88. package/src/lib/immutable-state/is-dev-mode.ts +0 -6
  89. package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +0 -260
  90. package/src/lib/immutable-state/with-immutable-state.ts +0 -115
  91. package/src/lib/mutation/http-mutation.spec.ts +0 -473
  92. package/src/lib/mutation/http-mutation.ts +0 -172
  93. package/src/lib/mutation/mutation.ts +0 -26
  94. package/src/lib/mutation/rx-mutation.spec.ts +0 -594
  95. package/src/lib/mutation/rx-mutation.ts +0 -208
  96. package/src/lib/shared/prettify.ts +0 -3
  97. package/src/lib/shared/throw-if-null.ts +0 -7
  98. package/src/lib/storage-sync/features/with-indexed-db.ts +0 -81
  99. package/src/lib/storage-sync/features/with-local-storage.ts +0 -58
  100. package/src/lib/storage-sync/internal/indexeddb.service.ts +0 -124
  101. package/src/lib/storage-sync/internal/local-storage.service.ts +0 -19
  102. package/src/lib/storage-sync/internal/models.ts +0 -62
  103. package/src/lib/storage-sync/internal/session-storage.service.ts +0 -18
  104. package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +0 -99
  105. package/src/lib/storage-sync/tests/with-storage-async.spec.ts +0 -308
  106. package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -268
  107. package/src/lib/storage-sync/with-storage-sync.ts +0 -233
  108. package/src/lib/with-call-state.spec.ts +0 -42
  109. package/src/lib/with-call-state.ts +0 -195
  110. package/src/lib/with-conditional.spec.ts +0 -125
  111. package/src/lib/with-data-service.spec.ts +0 -564
  112. package/src/lib/with-data-service.ts +0 -433
  113. package/src/lib/with-feature-factory.spec.ts +0 -69
  114. package/src/lib/with-mutations.spec.ts +0 -537
  115. package/src/lib/with-mutations.ts +0 -146
  116. package/src/lib/with-pagination.spec.ts +0 -90
  117. package/src/lib/with-pagination.ts +0 -353
  118. package/src/lib/with-redux.spec.ts +0 -258
  119. package/src/lib/with-redux.ts +0 -387
  120. package/src/lib/with-reset.spec.ts +0 -112
  121. package/src/lib/with-reset.ts +0 -62
  122. package/src/lib/with-undo-redo.spec.ts +0 -287
  123. package/src/lib/with-undo-redo.ts +0 -199
  124. package/src/test-setup.ts +0 -8
  125. package/tsconfig.json +0 -29
  126. package/tsconfig.lib.json +0 -17
  127. package/tsconfig.lib.prod.json +0 -9
  128. package/tsconfig.spec.json +0 -17
@@ -0,0 +1,2108 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, computed, isSignal, untracked, isDevMode as isDevMode$1, DestroyRef } from '@angular/core';
3
+ import { watchState, getState, signalStoreFeature, withMethods, withHooks, patchState as patchState$1, withState, withComputed, withProps } from '@ngrx/signals';
4
+ import { isPlatformBrowser, isPlatformServer } from '@angular/common';
5
+ import { Subject, switchMap, mergeMap, concatMap, exhaustMap, defer, tap, catchError, EMPTY, finalize, filter, map } from 'rxjs';
6
+ import { removeEntity, setAllEntities, updateEntity, addEntity } from '@ngrx/signals/entities';
7
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
8
+ import { HttpClient, HttpEventType, HttpResponse } from '@angular/common/http';
9
+
10
+ const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');
11
+ function createDevtoolsFeature(options) {
12
+ return {
13
+ [DEVTOOLS_FEATURE]: true,
14
+ ...options,
15
+ };
16
+ }
17
+
18
+ /**
19
+ * If multiple instances of the same SignalStore class
20
+ * exist, their devtool names are indexed.
21
+ *
22
+ * For example:
23
+ *
24
+ * ```typescript
25
+ * const Store = signalStore(
26
+ * withDevtools('flights')
27
+ * )
28
+ *
29
+ * const store1 = new Store(); // will show up as 'flights'
30
+ * const store2 = new Store(); // will show up as 'flights-1'
31
+ * ```
32
+ *
33
+ * With adding `withDisabledNameIndices` to the store:
34
+ * ```typescript
35
+ * const Store = signalStore(
36
+ * withDevtools('flights', withDisabledNameIndices())
37
+ * )
38
+ *
39
+ * const store1 = new Store(); // will show up as 'flights'
40
+ * const store2 = new Store(); //💥 throws an error
41
+ * ```
42
+ *
43
+ */
44
+ function withDisabledNameIndices() {
45
+ return createDevtoolsFeature({ indexNames: false });
46
+ }
47
+
48
+ function throwIfNull(obj) {
49
+ if (obj === null || obj === undefined) {
50
+ throw new Error('');
51
+ }
52
+ return obj;
53
+ }
54
+
55
+ /**
56
+ * Internal Service used by {@link withGlitchTracking}. It does not rely
57
+ * on `effect` as {@link DefaultTracker} does but uses the NgRx function
58
+ * `watchState` to track all state changes.
59
+ */
60
+ class GlitchTrackerService {
61
+ #stores = {};
62
+ #callback;
63
+ get stores() {
64
+ return Object.entries(this.#stores).reduce((acc, [id, { store }]) => {
65
+ acc[id] = store;
66
+ return acc;
67
+ }, {});
68
+ }
69
+ onChange(callback) {
70
+ this.#callback = callback;
71
+ }
72
+ removeStore(id) {
73
+ this.#stores = Object.entries(this.#stores).reduce((newStore, [storeId, value]) => {
74
+ if (storeId !== id) {
75
+ newStore[storeId] = value;
76
+ }
77
+ else {
78
+ value.destroyWatcher();
79
+ }
80
+ return newStore;
81
+ }, {});
82
+ throwIfNull(this.#callback)({});
83
+ }
84
+ track(id, store) {
85
+ const watcher = watchState(store, (state) => {
86
+ throwIfNull(this.#callback)({ [id]: state });
87
+ });
88
+ this.#stores[id] = { destroyWatcher: watcher.destroy, store };
89
+ }
90
+ notifyRenamedStore(id) {
91
+ if (Object.keys(this.#stores).includes(id) && this.#callback) {
92
+ this.#callback({ [id]: getState(this.#stores[id].store) });
93
+ }
94
+ }
95
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: GlitchTrackerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
96
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: GlitchTrackerService, providedIn: 'root' }); }
97
+ }
98
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: GlitchTrackerService, decorators: [{
99
+ type: Injectable,
100
+ args: [{ providedIn: 'root' }]
101
+ }] });
102
+
103
+ /**
104
+ * It tracks all state changes of the State, including intermediary updates
105
+ * that are typically suppressed by Angular's glitch-free mechanism.
106
+ *
107
+ * This feature is especially useful for debugging.
108
+ *
109
+ * Example:
110
+ *
111
+ * ```typescript
112
+ * const Store = signalStore(
113
+ * { providedIn: 'root' },
114
+ * withState({ count: 0 }),
115
+ * withDevtools('counter', withGlitchTracking()),
116
+ * withMethods((store) => ({
117
+ * increase: () =>
118
+ * patchState(store, (value) => ({ count: value.count + 1 })),
119
+ * }))
120
+ * );
121
+ *
122
+ * // would show up in the DevTools with value 0
123
+ * const store = inject(Store);
124
+ *
125
+ * store.increase(); // would show up in the DevTools with value 1
126
+ * store.increase(); // would show up in the DevTools with value 2
127
+ * store.increase(); // would show up in the DevTools with value 3
128
+ * ```
129
+ *
130
+ * Without `withGlitchTracking`, the DevTools would only show the final value of 3.
131
+ */
132
+ function withGlitchTracking() {
133
+ return createDevtoolsFeature({ tracker: GlitchTrackerService });
134
+ }
135
+
136
+ /**
137
+ * Allows you to define a function to map the state.
138
+ *
139
+ * It is needed for huge states, that slows down the Devtools and where
140
+ * you don't need to see the whole state or other reasons.
141
+ *
142
+ * Example:
143
+ *
144
+ * ```typescript
145
+ * const initialState = {
146
+ * id: 1,
147
+ * email: 'john.list@host.com',
148
+ * name: 'John List',
149
+ * enteredPassword: ''
150
+ * }
151
+ *
152
+ * const Store = signalStore(
153
+ * withState(initialState),
154
+ * withDevtools(
155
+ * 'user',
156
+ * withMapper(state => ({...state, enteredPassword: '***' }))
157
+ * )
158
+ * )
159
+ * ```
160
+ *
161
+ * @param map function which maps the state
162
+ */
163
+ function withMapper(map) {
164
+ return createDevtoolsFeature({ map: map });
165
+ }
166
+
167
+ /**
168
+ * Provides the configuration options for connecting to the Redux DevTools Extension.
169
+ */
170
+ function provideDevtoolsConfig(config) {
171
+ return {
172
+ provide: REDUX_DEVTOOLS_CONFIG,
173
+ useValue: config,
174
+ };
175
+ }
176
+ /**
177
+ * Injection token for the configuration options for connecting to the Redux DevTools Extension.
178
+ */
179
+ const REDUX_DEVTOOLS_CONFIG = new InjectionToken('ReduxDevtoolsConfig');
180
+
181
+ class DefaultTracker {
182
+ #stores = signal({});
183
+ get stores() {
184
+ return this.#stores();
185
+ }
186
+ #trackCallback;
187
+ #trackingEffect = effect(() => {
188
+ if (this.#trackCallback === undefined) {
189
+ throw new Error('no callback function defined');
190
+ }
191
+ const stores = this.#stores();
192
+ const fullState = Object.entries(stores).reduce((acc, [id, store]) => {
193
+ return { ...acc, [id]: getState(store) };
194
+ }, {});
195
+ this.#trackCallback(fullState);
196
+ });
197
+ track(id, store) {
198
+ this.#stores.update((value) => ({
199
+ ...value,
200
+ [id]: store,
201
+ }));
202
+ }
203
+ onChange(callback) {
204
+ this.#trackCallback = callback;
205
+ }
206
+ removeStore(id) {
207
+ this.#stores.update((stores) => Object.entries(stores).reduce((newStore, [storeId, state]) => {
208
+ if (storeId !== id) {
209
+ newStore[storeId] = state;
210
+ }
211
+ return newStore;
212
+ }, {}));
213
+ }
214
+ notifyRenamedStore(id) {
215
+ if (this.#stores()[id]) {
216
+ this.#stores.update((stores) => {
217
+ return { ...stores };
218
+ });
219
+ }
220
+ }
221
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DefaultTracker, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
222
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DefaultTracker, providedIn: 'root' }); }
223
+ }
224
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DefaultTracker, decorators: [{
225
+ type: Injectable,
226
+ args: [{ providedIn: 'root' }]
227
+ }] });
228
+
229
+ const currentActionNames = new Set();
230
+
231
+ const dummyConnection = {
232
+ send: () => void true,
233
+ };
234
+ /**
235
+ * A service provided by the root injector is
236
+ * required because the synchronization runs
237
+ * globally.
238
+ *
239
+ * The SignalStore could be provided in a component.
240
+ * If the effect starts in the injection
241
+ * context of the SignalStore, the complete sync
242
+ * process would shut down once the component gets
243
+ * destroyed.
244
+ */
245
+ class DevtoolsSyncer {
246
+ /**
247
+ * Stores all SignalStores that are connected to the
248
+ * DevTools along their options, names and id.
249
+ */
250
+ #stores = {};
251
+ #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
252
+ #trackers = [];
253
+ #devtoolsConfig = {
254
+ name: 'NgRx SignalStore',
255
+ ...inject(REDUX_DEVTOOLS_CONFIG, { optional: true }),
256
+ };
257
+ /**
258
+ * Maintains the current states of all stores to avoid conflicts
259
+ * between glitch-free and glitched trackers when used simultaneously.
260
+ *
261
+ * The challenge lies in ensuring that glitched trackers do not
262
+ * interfere with the synchronization process of glitch-free trackers.
263
+ * Specifically, glitched trackers could cause the synchronization to
264
+ * read the current state of stores managed by glitch-free trackers.
265
+ *
266
+ * Therefore, the synchronization process doesn't read the state from
267
+ * each store, but relies on #currentState.
268
+ *
269
+ * Please note, that here the key is the name and not the id.
270
+ */
271
+ #currentState = {};
272
+ #currentId = 1;
273
+ #connection = this.#isBrowser
274
+ ? window.__REDUX_DEVTOOLS_EXTENSION__
275
+ ? window.__REDUX_DEVTOOLS_EXTENSION__.connect(this.#devtoolsConfig)
276
+ : dummyConnection
277
+ : dummyConnection;
278
+ constructor() {
279
+ if (!this.#isBrowser) {
280
+ return;
281
+ }
282
+ }
283
+ ngOnDestroy() {
284
+ currentActionNames.clear();
285
+ }
286
+ syncToDevTools(changedStatePerId) {
287
+ const mappedChangedStatePerName = Object.entries(changedStatePerId).reduce((acc, [id, store]) => {
288
+ const { options, name } = this.#stores[id];
289
+ acc[name] = options.map(store);
290
+ return acc;
291
+ }, {});
292
+ this.#currentState = {
293
+ ...this.#currentState,
294
+ ...mappedChangedStatePerName,
295
+ };
296
+ const names = Array.from(currentActionNames);
297
+ const type = names.length ? names.join(', ') : 'Store Update';
298
+ currentActionNames.clear();
299
+ this.#connection.send({ type }, this.#currentState);
300
+ }
301
+ getNextId() {
302
+ return String(this.#currentId++);
303
+ }
304
+ /**
305
+ * Consumer provides the id. That is because we can only start
306
+ * tracking the store in the init hook.
307
+ * Unfortunately, methods for renaming having the final id
308
+ * need to be defined already before.
309
+ * That's why `withDevtools` requests first the id and
310
+ * then registers itself later.
311
+ */
312
+ addStore(id, name, store, options) {
313
+ let storeName = name;
314
+ const names = Object.values(this.#stores).map((store) => store.name);
315
+ if (names.includes(storeName)) {
316
+ // const { options } = throwIfNull(
317
+ // Object.values(this.#stores).find((store) => store.name === storeName)
318
+ // );
319
+ if (!options.indexNames) {
320
+ throw new Error(`An instance of the store ${storeName} already exists. \
321
+ Enable automatic indexing via withDevTools('${storeName}', { indexNames: true }), or rename it upon instantiation.`);
322
+ }
323
+ }
324
+ for (let i = 1; names.includes(storeName); i++) {
325
+ storeName = `${name}-${i}`;
326
+ }
327
+ this.#stores[id] = { name: storeName, options };
328
+ const tracker = options.tracker;
329
+ if (!this.#trackers.includes(tracker)) {
330
+ this.#trackers.push(tracker);
331
+ }
332
+ tracker.onChange((changedState) => this.syncToDevTools(changedState));
333
+ tracker.track(id, store);
334
+ }
335
+ removeStore(id) {
336
+ const name = this.#stores[id].name;
337
+ this.#stores = Object.entries(this.#stores).reduce((newStore, [storeId, value]) => {
338
+ if (storeId !== id) {
339
+ newStore[storeId] = value;
340
+ }
341
+ return newStore;
342
+ }, {});
343
+ this.#currentState = Object.entries(this.#currentState).reduce((newState, [storeName, state]) => {
344
+ if (storeName !== name) {
345
+ newState[storeName] = state;
346
+ }
347
+ return newState;
348
+ }, {});
349
+ for (const tracker of this.#trackers) {
350
+ tracker.removeStore(id);
351
+ }
352
+ }
353
+ /**
354
+ * Renames a store identified by its internal id. If the store has already
355
+ * been removed (e.g. due to component destruction), this is a no-op.
356
+ */
357
+ renameStore(id, newName) {
358
+ const storeEntry = this.#stores[id];
359
+ if (!storeEntry) {
360
+ return;
361
+ }
362
+ const oldName = storeEntry.name;
363
+ if (oldName === newName) {
364
+ return;
365
+ }
366
+ const otherStoreNames = Object.entries(this.#stores)
367
+ .filter(([entryId]) => entryId !== id)
368
+ .map(([, s]) => s.name);
369
+ if (otherStoreNames.includes(newName)) {
370
+ throw new Error(`NgRx Toolkit/DevTools: cannot rename from ${oldName} to ${newName}. ${newName} is already assigned to another SignalStore instance.`);
371
+ }
372
+ this.#stores = Object.entries(this.#stores).reduce((newStore, [entryId, value]) => {
373
+ if (entryId === id) {
374
+ newStore[entryId] = { ...value, name: newName };
375
+ }
376
+ else {
377
+ newStore[entryId] = value;
378
+ }
379
+ return newStore;
380
+ }, {});
381
+ // we don't rename in #currentState but wait for tracker to notify
382
+ // us with a changed state that contains that name.
383
+ this.#currentState = Object.entries(this.#currentState).reduce((newState, [storeName, state]) => {
384
+ if (storeName !== oldName) {
385
+ newState[storeName] = state;
386
+ }
387
+ return newState;
388
+ }, {});
389
+ this.#trackers.forEach((tracker) => tracker.notifyRenamedStore(id));
390
+ }
391
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DevtoolsSyncer, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
392
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DevtoolsSyncer, providedIn: 'root' }); }
393
+ }
394
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DevtoolsSyncer, decorators: [{
395
+ type: Injectable,
396
+ args: [{ providedIn: 'root' }]
397
+ }], ctorParameters: () => [] });
398
+
399
+ const renameDevtoolsMethodName = '___renameDevtoolsName';
400
+ const uniqueDevtoolsId = '___uniqueDevtoolsId';
401
+ const EXISTING_NAMES = new InjectionToken('Array contain existing names for the signal stores', { factory: () => [], providedIn: 'root' });
402
+ /**
403
+ * Adds this store as a feature state to the Redux DevTools.
404
+ *
405
+ * By default, the action name is 'Store Update'. You can
406
+ * change that via the {@link updateState} method, which has as second
407
+ * parameter the action name.
408
+ *
409
+ * The standalone function {@link renameDevtoolsName} can rename
410
+ * the store name.
411
+ *
412
+ * @param name name of the store as it should appear in the DevTools
413
+ * @param features features to extend or modify the behavior of the Devtools
414
+ */
415
+ function withDevtools(name, ...features) {
416
+ return signalStoreFeature(withMethods(() => {
417
+ const syncer = inject(DevtoolsSyncer);
418
+ const id = syncer.getNextId();
419
+ // TODO: use withProps and symbols
420
+ return {
421
+ [renameDevtoolsMethodName]: (newName) => {
422
+ syncer.renameStore(id, newName);
423
+ },
424
+ [uniqueDevtoolsId]: () => id,
425
+ };
426
+ }), withHooks((store) => {
427
+ const syncer = inject(DevtoolsSyncer);
428
+ const id = String(store[uniqueDevtoolsId]());
429
+ return {
430
+ onInit() {
431
+ const id = String(store[uniqueDevtoolsId]());
432
+ const finalOptions = {
433
+ indexNames: !features.some((f) => f.indexNames === false),
434
+ map: features.find((f) => f.map)?.map ?? ((state) => state),
435
+ tracker: inject(features.find((f) => f.tracker)?.tracker || DefaultTracker),
436
+ };
437
+ syncer.addStore(id, name, store, finalOptions);
438
+ },
439
+ onDestroy() {
440
+ syncer.removeStore(id);
441
+ },
442
+ };
443
+ }));
444
+ }
445
+
446
+ /**
447
+ * Renames the name of a store how it appears in the Devtools.
448
+ * @param store instance of the SignalStore
449
+ * @param newName new name for the Devtools
450
+ */
451
+ function renameDevtoolsName(store, newName) {
452
+ const renameMethod = store[renameDevtoolsMethodName];
453
+ if (!renameMethod) {
454
+ throw new Error("Devtools extensions haven't been added to this store.");
455
+ }
456
+ renameMethod(newName);
457
+ }
458
+
459
+ /**
460
+ * @deprecated Has been renamed to `updateState`
461
+ */
462
+ const patchState = (state, action, ...rest) => {
463
+ updateState(state, action, ...rest);
464
+ };
465
+ /**
466
+ * Wrapper of `patchState` for DevTools integration. Next to updating the state,
467
+ * it also sends the action to the DevTools.
468
+ * @param stateSource state of Signal Store
469
+ * @param action name of action how it will show in DevTools
470
+ * @param updaters updater functions or objects
471
+ */
472
+ function updateState(stateSource, action, ...updaters) {
473
+ currentActionNames.add(action);
474
+ return patchState$1(stateSource, ...updaters);
475
+ }
476
+
477
+ /**
478
+ * Stub for DevTools integration. Can be used to disable DevTools in production.
479
+ */
480
+ const withDevToolsStub = () => (store) => store;
481
+
482
+ function assertActionFnSpecs(obj) {
483
+ if (!obj || typeof obj !== 'object') {
484
+ throw new Error('%o is not an Action Specification');
485
+ }
486
+ }
487
+
488
+ function payload() {
489
+ return {};
490
+ }
491
+ const noPayload = {};
492
+ /**
493
+ * Creates a reducer function to separate the reducer logic into another file.
494
+ *
495
+ * ```typescript
496
+ * interface FlightState {
497
+ * flights: Flight[];
498
+ * effect1: boolean;
499
+ * effect2: boolean;
500
+ * }
501
+ *
502
+ * const initialState: FlightState = {
503
+ * flights: [],
504
+ * effect1: false,
505
+ * effect2: false,
506
+ * };
507
+ *
508
+ * const actions = {
509
+ * init: noPayload,
510
+ * updateEffect1: payload<{ value: boolean }>(),
511
+ * updateEffect2: payload<{ value: boolean }>(),
512
+ * };
513
+ *
514
+ * const reducer = createReducer<FlightState, typeof actions>((actions, on) => {
515
+ * on(actions.updateEffect1, (state, { value }) => {
516
+ * patchState(state, { effect1: value });
517
+ * });
518
+ *
519
+ * on(actions.updateEffect2, (state, { value }) => {
520
+ * patchState(state, { effect2: value });
521
+ * });
522
+ * });
523
+ *
524
+ * signalStore(
525
+ * withState(initialState),
526
+ * withRedux({
527
+ * actions,
528
+ * reducer,
529
+ * })
530
+ * );
531
+ * ```
532
+ * @param reducerFactory
533
+ */
534
+ function createReducer(reducerFactory) {
535
+ return reducerFactory;
536
+ }
537
+ /**
538
+ * @deprecated Use NgRx's `@ngrx/signals/events` starting in 19.2
539
+ *
540
+ * Creates the effects function to separate the effects logic into another file.
541
+ *
542
+ * ```typescript
543
+ * interface FlightState {
544
+ * flights: Flight[];
545
+ * effect1: boolean;
546
+ * effect2: boolean;
547
+ * }
548
+ *
549
+ * const initialState: FlightState = {
550
+ * flights: [],
551
+ * effect1: false,
552
+ * effect2: false,
553
+ * };
554
+ *
555
+ * const actions = {
556
+ * init: noPayload,
557
+ * updateEffect1: payload<{ value: boolean }>(),
558
+ * updateEffect2: payload<{ value: boolean }>(),
559
+ * };
560
+ *
561
+ * const effects = createEffects(actions, (actions, create) => {
562
+ * return {
563
+ * init1$: create(actions.init).pipe(
564
+ * map(() => actions.updateEffect1({ value: true }))
565
+ * ),
566
+ * init2$: create(actions.init).pipe(
567
+ * map(() => actions.updateEffect2({ value: true }))
568
+ * ),
569
+ * };
570
+ * });
571
+ *
572
+ * signalStore(
573
+ * withState(initialState),
574
+ * withRedux({
575
+ * actions,
576
+ * effects,
577
+ * })
578
+ * );
579
+ * ```
580
+ * @param actions
581
+ * @param effectsFactory
582
+ */
583
+ function createEffects(actions, effectsFactory) {
584
+ return effectsFactory;
585
+ }
586
+ function createActionFns(actionFnSpecs, reducerRegistry, effectsRegistry, state) {
587
+ const actionFns = {};
588
+ for (const type in actionFnSpecs) {
589
+ const actionFn = (payload) => {
590
+ const fullPayload = { ...payload, type };
591
+ const reducer = reducerRegistry[type];
592
+ if (reducer) {
593
+ reducer(state, fullPayload);
594
+ }
595
+ const effectSubjects = effectsRegistry[type];
596
+ if (effectSubjects?.length) {
597
+ for (const effectSubject of effectSubjects) {
598
+ effectSubject.next(fullPayload);
599
+ }
600
+ }
601
+ return fullPayload;
602
+ };
603
+ actionFn.type = type.toString();
604
+ actionFns[type] = actionFn;
605
+ }
606
+ return actionFns;
607
+ }
608
+ function createPublicAndAllActionsFns(actionFnSpecs, reducerRegistry, effectsRegistry, state) {
609
+ if ('public' in actionFnSpecs || 'private' in actionFnSpecs) {
610
+ const privates = actionFnSpecs['private'] || {};
611
+ const publics = actionFnSpecs['public'] || {};
612
+ assertActionFnSpecs(privates);
613
+ assertActionFnSpecs(publics);
614
+ const privateActionFns = createActionFns(privates, reducerRegistry, effectsRegistry, state);
615
+ const publicActionFns = createActionFns(publics, reducerRegistry, effectsRegistry, state);
616
+ return {
617
+ all: { ...privateActionFns, ...publicActionFns },
618
+ publics: publicActionFns,
619
+ };
620
+ }
621
+ const actionFns = createActionFns(actionFnSpecs, reducerRegistry, effectsRegistry, state);
622
+ return { all: actionFns, publics: actionFns };
623
+ }
624
+ function fillReducerRegistry(reducer, actionFns, reducerRegistry) {
625
+ function on(action, reducerFn) {
626
+ reducerRegistry[action.type] = reducerFn;
627
+ }
628
+ reducer(actionFns, on);
629
+ return reducerRegistry;
630
+ }
631
+ function fillEffects(effects, actionFns, effectsRegistry = {}) {
632
+ function create(action) {
633
+ const subject = new Subject();
634
+ if (!(action.type in effectsRegistry)) {
635
+ effectsRegistry[action.type] = [];
636
+ }
637
+ effectsRegistry[action.type].push(subject);
638
+ return subject.asObservable();
639
+ }
640
+ const effectObservables = effects(actionFns, create);
641
+ return Object.values(effectObservables);
642
+ }
643
+ function startSubscriptions(observables) {
644
+ return observables.map((observable) => observable.subscribe());
645
+ }
646
+ function processRedux(actionFnSpecs, reducer, effects, store) {
647
+ const reducerRegistry = {};
648
+ const effectsRegistry = {};
649
+ const actionsMap = createPublicAndAllActionsFns(actionFnSpecs, reducerRegistry, effectsRegistry, store);
650
+ const actionFns = actionsMap.all;
651
+ const publicActionsFns = actionsMap.publics;
652
+ fillReducerRegistry(reducer, actionFns, reducerRegistry);
653
+ const effectObservables = fillEffects(effects, actionFns, effectsRegistry);
654
+ const subscriptions = startSubscriptions(effectObservables);
655
+ return {
656
+ methods: publicActionsFns,
657
+ subscriptions: subscriptions,
658
+ };
659
+ }
660
+ /**
661
+ * @param redux redux
662
+ *
663
+ * properties do not start with `with` since they are not extension functions on their own.
664
+ *
665
+ * no dependency to NgRx
666
+ *
667
+ * actions are passed to reducer and effects, but it is also possible to use other actions.
668
+ * effects provide forAction and do not return anything. that is important because effects should stay inaccessible
669
+ */
670
+ function withRedux(redux) {
671
+ return (store) => {
672
+ const { methods } = processRedux(redux.actions, redux.reducer, redux.effects, store);
673
+ return {
674
+ ...store,
675
+ methods: { ...store.methods, ...methods },
676
+ };
677
+ };
678
+ }
679
+
680
+ function deriveCallStateKeys(collection) {
681
+ return {
682
+ callStateKey: collection ? `${collection}CallState` : 'callState',
683
+ loadingKey: collection ? `${collection}Loading` : 'loading',
684
+ loadedKey: collection ? `${collection}Loaded` : 'loaded',
685
+ errorKey: collection ? `${collection}Error` : 'error',
686
+ };
687
+ }
688
+ function getCallStateKeys(config) {
689
+ const prop = config?.collection;
690
+ return deriveCallStateKeys(prop);
691
+ }
692
+ function getCollectionArray(config) {
693
+ return 'collections' in config
694
+ ? config.collections
695
+ : 'collection' in config && config.collection
696
+ ? [config.collection]
697
+ : undefined;
698
+ }
699
+ function withCallState(config) {
700
+ return signalStoreFeature(withState(() => {
701
+ if (!config) {
702
+ return { callState: 'init' };
703
+ }
704
+ const collections = getCollectionArray(config);
705
+ if (collections) {
706
+ return collections.reduce((acc, cur) => ({
707
+ ...acc,
708
+ ...{ [cur ? `${cur}CallState` : 'callState']: 'init' },
709
+ }), {});
710
+ }
711
+ return { callState: 'init' };
712
+ }), withComputed((state) => {
713
+ if (config) {
714
+ const collections = getCollectionArray(config);
715
+ if (collections) {
716
+ return collections.reduce((acc, cur) => {
717
+ const { callStateKey, errorKey, loadedKey, loadingKey } = deriveCallStateKeys(cur);
718
+ const callState = state[callStateKey];
719
+ return {
720
+ ...acc,
721
+ [loadingKey]: computed(() => callState() === 'loading'),
722
+ [loadedKey]: computed(() => callState() === 'loaded'),
723
+ [errorKey]: computed(() => {
724
+ const v = callState();
725
+ return typeof v === 'object' ? v.error : null;
726
+ }),
727
+ };
728
+ }, {});
729
+ }
730
+ }
731
+ const { callStateKey, errorKey, loadedKey, loadingKey } = deriveCallStateKeys();
732
+ const callState = state[callStateKey];
733
+ return {
734
+ [loadingKey]: computed(() => callState() === 'loading'),
735
+ [loadedKey]: computed(() => callState() === 'loaded'),
736
+ [errorKey]: computed(() => {
737
+ const v = callState();
738
+ return typeof v === 'object' ? v.error : null;
739
+ }),
740
+ };
741
+ }));
742
+ }
743
+ function setLoading(prop) {
744
+ if (prop) {
745
+ return { [`${prop}CallState`]: 'loading' };
746
+ }
747
+ return { callState: 'loading' };
748
+ }
749
+ function setLoaded(prop) {
750
+ if (prop) {
751
+ return { [`${prop}CallState`]: 'loaded' };
752
+ }
753
+ else {
754
+ return { callState: 'loaded' };
755
+ }
756
+ }
757
+ function setError(error, prop) {
758
+ let errorMessage;
759
+ if (!error) {
760
+ errorMessage = '';
761
+ }
762
+ else if (typeof error === 'object' && 'message' in error) {
763
+ errorMessage = String(error.message);
764
+ }
765
+ else {
766
+ errorMessage = String(error);
767
+ }
768
+ if (prop) {
769
+ return {
770
+ [`${prop}CallState`]: { error: errorMessage },
771
+ };
772
+ }
773
+ else {
774
+ return { callState: { error: errorMessage } };
775
+ }
776
+ }
777
+
778
+ function capitalize(str) {
779
+ return str ? str[0].toUpperCase() + str.substring(1) : str;
780
+ }
781
+ function getDataServiceKeys(options) {
782
+ const filterKey = options.collection
783
+ ? `${options.collection}Filter`
784
+ : 'filter';
785
+ const selectedIdsKey = options.collection
786
+ ? `selected${capitalize(options.collection)}Ids`
787
+ : 'selectedIds';
788
+ const selectedEntitiesKey = options.collection
789
+ ? `selected${capitalize(options.collection)}Entities`
790
+ : 'selectedEntities';
791
+ const updateFilterKey = options.collection
792
+ ? `update${capitalize(options.collection)}Filter`
793
+ : 'updateFilter';
794
+ const updateSelectedKey = options.collection
795
+ ? `updateSelected${capitalize(options.collection)}Entities`
796
+ : 'updateSelected';
797
+ const loadKey = options.collection
798
+ ? `load${capitalize(options.collection)}Entities`
799
+ : 'load';
800
+ const currentKey = options.collection
801
+ ? `current${capitalize(options.collection)}`
802
+ : 'current';
803
+ const loadByIdKey = options.collection
804
+ ? `load${capitalize(options.collection)}ById`
805
+ : 'loadById';
806
+ const setCurrentKey = options.collection
807
+ ? `setCurrent${capitalize(options.collection)}`
808
+ : 'setCurrent';
809
+ const createKey = options.collection
810
+ ? `create${capitalize(options.collection)}`
811
+ : 'create';
812
+ const updateKey = options.collection
813
+ ? `update${capitalize(options.collection)}`
814
+ : 'update';
815
+ const updateAllKey = options.collection
816
+ ? `updateAll${capitalize(options.collection)}`
817
+ : 'updateAll';
818
+ const deleteKey = options.collection
819
+ ? `delete${capitalize(options.collection)}`
820
+ : 'delete';
821
+ // TODO: Take these from @ngrx/signals/entities, when they are exported
822
+ const entitiesKey = options.collection
823
+ ? `${options.collection}Entities`
824
+ : 'entities';
825
+ const entityMapKey = options.collection
826
+ ? `${options.collection}EntityMap`
827
+ : 'entityMap';
828
+ const idsKey = options.collection ? `${options.collection}Ids` : 'ids';
829
+ return {
830
+ filterKey,
831
+ selectedIdsKey,
832
+ selectedEntitiesKey,
833
+ updateFilterKey,
834
+ updateSelectedKey,
835
+ loadKey,
836
+ entitiesKey,
837
+ entityMapKey,
838
+ idsKey,
839
+ currentKey,
840
+ loadByIdKey,
841
+ setCurrentKey,
842
+ createKey,
843
+ updateKey,
844
+ updateAllKey,
845
+ deleteKey,
846
+ };
847
+ }
848
+ function withDataService(options) {
849
+ const { dataServiceType, filter, collection: prefix } = options;
850
+ const { entitiesKey, filterKey, loadKey, selectedEntitiesKey, selectedIdsKey, updateFilterKey, updateSelectedKey, currentKey, createKey, updateKey, updateAllKey, deleteKey, loadByIdKey, setCurrentKey, } = getDataServiceKeys(options);
851
+ const { callStateKey } = getCallStateKeys({ collection: prefix });
852
+ return signalStoreFeature(withState(() => ({
853
+ [filterKey]: filter,
854
+ [selectedIdsKey]: {},
855
+ [currentKey]: undefined,
856
+ })), withComputed((store) => {
857
+ const entities = store[entitiesKey];
858
+ const selectedIds = store[selectedIdsKey];
859
+ return {
860
+ [selectedEntitiesKey]: computed(() => entities().filter((e) => selectedIds()[e.id])),
861
+ };
862
+ }), withMethods((store) => {
863
+ const dataService = inject(dataServiceType);
864
+ return {
865
+ [updateFilterKey]: (filter) => {
866
+ patchState$1(store, { [filterKey]: filter });
867
+ },
868
+ [updateSelectedKey]: (id, selected) => {
869
+ patchState$1(store, (state) => ({
870
+ [selectedIdsKey]: {
871
+ ...state[selectedIdsKey],
872
+ [id]: selected,
873
+ },
874
+ }));
875
+ },
876
+ [loadKey]: async () => {
877
+ const filter = store[filterKey];
878
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
879
+ try {
880
+ const result = await dataService.load(filter());
881
+ patchState$1(store, prefix
882
+ ? setAllEntities(result, { collection: prefix })
883
+ : setAllEntities(result));
884
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
885
+ }
886
+ catch (e) {
887
+ (() => store[callStateKey] &&
888
+ patchState$1(store, setError(e, prefix)))();
889
+ throw e;
890
+ }
891
+ },
892
+ [loadByIdKey]: async (id) => {
893
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
894
+ try {
895
+ const current = await dataService.loadById(id);
896
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
897
+ patchState$1(store, { [currentKey]: current });
898
+ }
899
+ catch (e) {
900
+ (() => store[callStateKey] &&
901
+ patchState$1(store, setError(e, prefix)))();
902
+ throw e;
903
+ }
904
+ },
905
+ [setCurrentKey]: (current) => {
906
+ patchState$1(store, { [currentKey]: current });
907
+ },
908
+ [createKey]: async (entity) => {
909
+ patchState$1(store, { [currentKey]: entity });
910
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
911
+ try {
912
+ const created = await dataService.create(entity);
913
+ patchState$1(store, { [currentKey]: created });
914
+ patchState$1(store, prefix
915
+ ? addEntity(created, { collection: prefix })
916
+ : addEntity(created));
917
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
918
+ }
919
+ catch (e) {
920
+ (() => store[callStateKey] &&
921
+ patchState$1(store, setError(e, prefix)))();
922
+ throw e;
923
+ }
924
+ },
925
+ [updateKey]: async (entity) => {
926
+ patchState$1(store, { [currentKey]: entity });
927
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
928
+ try {
929
+ const updated = await dataService.update(entity);
930
+ patchState$1(store, { [currentKey]: updated });
931
+ const updateArg = {
932
+ id: updated.id,
933
+ changes: updated,
934
+ };
935
+ const updater = (collection) => updateEntity(updateArg, { collection });
936
+ patchState$1(store, prefix ? updater(prefix) : updateEntity(updateArg));
937
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
938
+ }
939
+ catch (e) {
940
+ (() => store[callStateKey] &&
941
+ patchState$1(store, setError(e, prefix)))();
942
+ throw e;
943
+ }
944
+ },
945
+ [updateAllKey]: async (entities) => {
946
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
947
+ try {
948
+ const result = await dataService.updateAll(entities);
949
+ patchState$1(store, prefix
950
+ ? setAllEntities(result, { collection: prefix })
951
+ : setAllEntities(result));
952
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
953
+ }
954
+ catch (e) {
955
+ (() => store[callStateKey] &&
956
+ patchState$1(store, setError(e, prefix)))();
957
+ throw e;
958
+ }
959
+ },
960
+ [deleteKey]: async (entity) => {
961
+ patchState$1(store, { [currentKey]: entity });
962
+ (() => store[callStateKey] && patchState$1(store, setLoading(prefix)))();
963
+ try {
964
+ await dataService.delete(entity);
965
+ patchState$1(store, { [currentKey]: undefined });
966
+ patchState$1(store, prefix
967
+ ? removeEntity(entity.id, { collection: prefix })
968
+ : removeEntity(entity.id));
969
+ (() => store[callStateKey] && patchState$1(store, setLoaded(prefix)))();
970
+ }
971
+ catch (e) {
972
+ (() => store[callStateKey] &&
973
+ patchState$1(store, setError(e, prefix)))();
974
+ throw e;
975
+ }
976
+ },
977
+ };
978
+ }));
979
+ }
980
+
981
+ /** With pagination comes in two flavors the first one is local pagination or in memory pagination. For example we have 2000 items which we want
982
+ * to display in a table and the response payload is small enough to be stored in the memory. But we can not display all 2000 items at once
983
+ * so we need to paginate the data. The second flavor is server side pagination where the response payload is too large to be stored in the memory
984
+ * and we need to fetch the data from the server in chunks. In the second case we 'could' also cache the data in the memory but that could lead to
985
+ * other problems like memory leaks and stale data. So we will not cache the data in the memory in the second case.
986
+ * This feature implements the local pagination.
987
+ */
988
+ function withPagination(options) {
989
+ const { pageKey, pageSizeKey, entitiesKey, selectedPageEntitiesKey, totalCountKey, pageCountKey, pageNavigationArrayMaxKey, pageNavigationArrayKey, hasNextPageKey, hasPreviousPageKey, } = createPaginationKeys(options);
990
+ return signalStoreFeature(withState({
991
+ [pageKey]: 0,
992
+ [pageSizeKey]: 10,
993
+ [pageNavigationArrayMaxKey]: 7,
994
+ }), withComputed((store) => {
995
+ const entities = store[entitiesKey];
996
+ const page = store[pageKey];
997
+ const pageSize = store[pageSizeKey];
998
+ const pageNavigationArrayMax = store[pageNavigationArrayMaxKey];
999
+ return {
1000
+ // The derived enitites which are displayed on the current page
1001
+ [selectedPageEntitiesKey]: computed(() => {
1002
+ const pageSizeValue = pageSize();
1003
+ const pageValue = page();
1004
+ return entities().slice(pageValue * pageSizeValue, (pageValue + 1) * pageSizeValue);
1005
+ }),
1006
+ [totalCountKey]: computed(() => entities().length),
1007
+ [pageCountKey]: computed(() => {
1008
+ const totalCountValue = entities().length;
1009
+ const pageSizeValue = pageSize();
1010
+ if (totalCountValue === 0) {
1011
+ return 0;
1012
+ }
1013
+ return Math.ceil(totalCountValue / pageSizeValue);
1014
+ }),
1015
+ [pageNavigationArrayKey]: computed(() => createPageArray(page(), pageSize(), entities().length, pageNavigationArrayMax())),
1016
+ [hasNextPageKey]: computed(() => {
1017
+ return page() < pageSize();
1018
+ }),
1019
+ [hasPreviousPageKey]: computed(() => {
1020
+ return page() > 1;
1021
+ }),
1022
+ };
1023
+ }));
1024
+ }
1025
+ function gotoPage(page, options) {
1026
+ const { pageKey } = createPaginationKeys(options);
1027
+ return {
1028
+ [pageKey]: page,
1029
+ };
1030
+ }
1031
+ function setPageSize(pageSize, options) {
1032
+ const { pageSizeKey } = createPaginationKeys(options);
1033
+ return {
1034
+ [pageSizeKey]: pageSize,
1035
+ };
1036
+ }
1037
+ function nextPage(options) {
1038
+ const { pageKey } = createPaginationKeys(options);
1039
+ return {
1040
+ [pageKey]: (currentPage) => currentPage + 1,
1041
+ };
1042
+ }
1043
+ function previousPage(options) {
1044
+ const { pageKey } = createPaginationKeys(options);
1045
+ return {
1046
+ [pageKey]: (currentPage) => Math.max(currentPage - 1, 1),
1047
+ };
1048
+ }
1049
+ function firstPage(options) {
1050
+ const { pageKey } = createPaginationKeys(options);
1051
+ return {
1052
+ [pageKey]: 1,
1053
+ };
1054
+ }
1055
+ function setMaxPageNavigationArrayItems(maxPageNavigationArrayItems, options) {
1056
+ const { pageNavigationArrayMaxKey } = createPaginationKeys(options);
1057
+ return {
1058
+ [pageNavigationArrayMaxKey]: maxPageNavigationArrayItems,
1059
+ };
1060
+ }
1061
+ function createPaginationKeys(options) {
1062
+ const entitiesKey = options?.collection
1063
+ ? `${options.collection}Entities`
1064
+ : 'entities';
1065
+ const selectedPageEntitiesKey = options?.collection
1066
+ ? `selectedPage${capitalize(options?.collection)}Entities`
1067
+ : 'selectedPageEntities';
1068
+ const pageKey = options?.collection
1069
+ ? `${options.collection}CurrentPage`
1070
+ : 'currentPage';
1071
+ const pageSizeKey = options?.collection
1072
+ ? `${options.collection}PageSize`
1073
+ : 'pageSize';
1074
+ const totalCountKey = options?.collection
1075
+ ? `${options.collection}TotalCount`
1076
+ : 'totalCount';
1077
+ const pageCountKey = options?.collection
1078
+ ? `${options.collection}PageCount`
1079
+ : 'pageCount';
1080
+ const pageNavigationArrayMaxKey = options?.collection
1081
+ ? `${options.collection}PageNavigationArrayMax`
1082
+ : 'pageNavigationArrayMax';
1083
+ const pageNavigationArrayKey = options?.collection
1084
+ ? `${options.collection}PageNavigationArray`
1085
+ : 'pageNavigationArray';
1086
+ const hasNextPageKey = options?.collection
1087
+ ? `hasNext${capitalize(options.collection)}Page`
1088
+ : 'hasNextPage';
1089
+ const hasPreviousPageKey = options?.collection
1090
+ ? `hasPrevious${capitalize(options.collection)}Page`
1091
+ : 'hasPreviousPage';
1092
+ return {
1093
+ pageKey,
1094
+ pageSizeKey,
1095
+ entitiesKey,
1096
+ selectedPageEntitiesKey,
1097
+ totalCountKey,
1098
+ pageCountKey,
1099
+ pageNavigationArrayKey,
1100
+ pageNavigationArrayMaxKey,
1101
+ hasNextPageKey,
1102
+ hasPreviousPageKey,
1103
+ };
1104
+ }
1105
+ function createPageArray(currentPage, itemsPerPage, totalItems, paginationRange) {
1106
+ // Convert paginationRange to number in case it's a string
1107
+ paginationRange = +paginationRange;
1108
+ // Calculate total number of pages
1109
+ const totalPages = Math.max(Math.ceil(totalItems / itemsPerPage), 1);
1110
+ const halfWay = Math.ceil(paginationRange / 2);
1111
+ const isStart = currentPage <= halfWay;
1112
+ const isEnd = totalPages - halfWay < currentPage;
1113
+ const isMiddle = !isStart && !isEnd;
1114
+ const ellipsesNeeded = paginationRange < totalPages;
1115
+ const pages = [];
1116
+ for (let i = 1; i <= totalPages && i <= paginationRange; i++) {
1117
+ let pageNumber = i;
1118
+ if (i === paginationRange) {
1119
+ pageNumber = totalPages;
1120
+ }
1121
+ else if (ellipsesNeeded) {
1122
+ if (isEnd) {
1123
+ pageNumber = totalPages - paginationRange + i;
1124
+ }
1125
+ else if (isMiddle) {
1126
+ pageNumber = currentPage - halfWay + i;
1127
+ }
1128
+ }
1129
+ const openingEllipsesNeeded = i === 2 && (isMiddle || isEnd);
1130
+ const closingEllipsesNeeded = i === paginationRange - 1 && (isMiddle || isStart);
1131
+ const label = ellipsesNeeded && (openingEllipsesNeeded || closingEllipsesNeeded)
1132
+ ? '...'
1133
+ : pageNumber;
1134
+ pages.push({ label, value: pageNumber });
1135
+ }
1136
+ return pages;
1137
+ }
1138
+
1139
+ /**
1140
+ * Adds a `resetState` method to the store, which resets the state
1141
+ * to the initial state.
1142
+ *
1143
+ * If you want to set a custom initial state, you can use {@link setResetState}.
1144
+ */
1145
+ function withReset() {
1146
+ return signalStoreFeature(withProps(() => ({ _resetState: { value: {} } })), withMethods((store) => {
1147
+ // workaround to TS excessive property check
1148
+ const methods = {
1149
+ resetState() {
1150
+ patchState$1(store, store._resetState.value);
1151
+ },
1152
+ __setResetState__(state) {
1153
+ store._resetState.value = state;
1154
+ },
1155
+ };
1156
+ return methods;
1157
+ }), withHooks((store) => ({
1158
+ onInit() {
1159
+ store._resetState.value = getState(store);
1160
+ },
1161
+ })));
1162
+ }
1163
+ /**
1164
+ * Sets the reset state of the store to the given state.
1165
+ *
1166
+ * Throws an error if the store is not configured with {@link withReset}.
1167
+ * @param store the instance of a SignalStore
1168
+ * @param state the state to set as the reset state
1169
+ */
1170
+ function setResetState(store, state) {
1171
+ if (!('__setResetState__' in store)) {
1172
+ throw new Error('Cannot set reset state, since store is not configured with withReset()');
1173
+ }
1174
+ store.__setResetState__(state);
1175
+ }
1176
+
1177
+ const defaultOptions = {
1178
+ maxStackSize: 100,
1179
+ keys: [],
1180
+ skip: 0,
1181
+ };
1182
+ function getUndoRedoKeys(collections) {
1183
+ if (collections) {
1184
+ return collections.flatMap((c) => [
1185
+ `${c}EntityMap`,
1186
+ `${c}Ids`,
1187
+ `selected${capitalize(c)}Ids`,
1188
+ `${c}Filter`,
1189
+ ]);
1190
+ }
1191
+ return ['entityMap', 'ids', 'selectedIds', 'filter'];
1192
+ }
1193
+ function withUndoRedo(options) {
1194
+ let previous = null;
1195
+ let skipOnce = false;
1196
+ const normalized = {
1197
+ ...defaultOptions,
1198
+ ...options,
1199
+ };
1200
+ //
1201
+ // Design Decision: This feature has its own
1202
+ // internal state.
1203
+ //
1204
+ const undoStack = [];
1205
+ const redoStack = [];
1206
+ const canUndo = signal(false);
1207
+ const canRedo = signal(false);
1208
+ const updateInternal = () => {
1209
+ canUndo.set(undoStack.length !== 0);
1210
+ canRedo.set(redoStack.length !== 0);
1211
+ };
1212
+ const keys = [...getUndoRedoKeys(normalized.collections), ...normalized.keys];
1213
+ return signalStoreFeature(withComputed(() => ({
1214
+ canUndo: canUndo.asReadonly(),
1215
+ canRedo: canRedo.asReadonly(),
1216
+ })), withMethods((store) => ({
1217
+ undo() {
1218
+ const item = undoStack.pop();
1219
+ if (item && previous) {
1220
+ redoStack.push(previous);
1221
+ }
1222
+ if (item) {
1223
+ skipOnce = true;
1224
+ patchState$1(store, item);
1225
+ previous = item;
1226
+ }
1227
+ updateInternal();
1228
+ },
1229
+ redo() {
1230
+ const item = redoStack.pop();
1231
+ if (item && previous) {
1232
+ undoStack.push(previous);
1233
+ }
1234
+ if (item) {
1235
+ skipOnce = true;
1236
+ patchState$1(store, item);
1237
+ previous = item;
1238
+ }
1239
+ updateInternal();
1240
+ },
1241
+ clearStack() {
1242
+ undoStack.splice(0);
1243
+ redoStack.splice(0);
1244
+ previous = null;
1245
+ updateInternal();
1246
+ },
1247
+ })), withHooks({
1248
+ onInit(store) {
1249
+ effect(() => {
1250
+ const cand = keys.reduce((acc, key) => {
1251
+ const s = store[key];
1252
+ if (s && isSignal(s)) {
1253
+ return {
1254
+ ...acc,
1255
+ [key]: s(),
1256
+ };
1257
+ }
1258
+ return acc;
1259
+ }, {});
1260
+ if (normalized.skip > 0) {
1261
+ normalized.skip--;
1262
+ return;
1263
+ }
1264
+ if (skipOnce) {
1265
+ skipOnce = false;
1266
+ return;
1267
+ }
1268
+ //
1269
+ // Deep Comparison to prevent duplicated entries
1270
+ // on the stack. This can e.g. happen after an undo
1271
+ // if the component sends back the undone filter
1272
+ // to the store.
1273
+ //
1274
+ if (JSON.stringify(cand) === JSON.stringify(previous)) {
1275
+ return;
1276
+ }
1277
+ // Clear redoStack after recorded action
1278
+ redoStack.splice(0);
1279
+ if (previous) {
1280
+ undoStack.push(previous);
1281
+ }
1282
+ if (redoStack.length > normalized.maxStackSize) {
1283
+ undoStack.unshift();
1284
+ }
1285
+ previous = cand;
1286
+ // Don't propogate current reactive context
1287
+ untracked(() => updateInternal());
1288
+ });
1289
+ },
1290
+ }));
1291
+ }
1292
+
1293
+ /**
1294
+ * Deep freezes a state object along its properties with primitive values
1295
+ * on the first level.
1296
+ *
1297
+ * The reason for this is that the final state is a merge of all
1298
+ * root properties of all states, i.e. `withState`,....
1299
+ *
1300
+ * Since the root object will not be part of the state (shadow clone),
1301
+ * we are not freezing it.
1302
+ */
1303
+ function deepFreeze(target,
1304
+ // if empty all properties will be frozen
1305
+ propertyNamesToBeFrozen,
1306
+ // also means that we are on the first level
1307
+ isRoot = true) {
1308
+ const runPropertyNameCheck = propertyNamesToBeFrozen.length > 0;
1309
+ for (const key of Reflect.ownKeys(target)) {
1310
+ if (runPropertyNameCheck && !propertyNamesToBeFrozen.includes(key)) {
1311
+ continue;
1312
+ }
1313
+ const propValue = target[key];
1314
+ if (isRecordLike(propValue) && !Object.isFrozen(propValue)) {
1315
+ Object.freeze(propValue);
1316
+ deepFreeze(propValue, [], false);
1317
+ }
1318
+ else if (isRoot) {
1319
+ Object.defineProperty(target, key, {
1320
+ value: propValue,
1321
+ writable: false,
1322
+ configurable: false,
1323
+ });
1324
+ }
1325
+ }
1326
+ }
1327
+ function isRecordLike(target) {
1328
+ return typeof target === 'object' && target !== null;
1329
+ }
1330
+
1331
+ // necessary wrapper function to test prod mode
1332
+ function isDevMode() {
1333
+ return isDevMode$1();
1334
+ }
1335
+
1336
+ function withImmutableState(stateOrFactory, options) {
1337
+ const immutableState = typeof stateOrFactory === 'function' ? stateOrFactory() : stateOrFactory;
1338
+ const stateKeys = Reflect.ownKeys(immutableState);
1339
+ const applyFreezing = isDevMode() || options?.enableInProduction === true;
1340
+ return signalStoreFeature(withState(immutableState), withHooks((store) => ({
1341
+ onInit() {
1342
+ if (!applyFreezing) {
1343
+ return;
1344
+ }
1345
+ /**
1346
+ * `immutableState` will be initially frozen. That is because
1347
+ * of potential mutations outside the SignalStore
1348
+ *
1349
+ * ```ts
1350
+ * const initialState = {id: 1};
1351
+ * signalStore(withImmutableState(initialState));
1352
+ *
1353
+ * initialState.id = 2; // must throw immutability
1354
+ * ```
1355
+ */
1356
+ Object.freeze(immutableState);
1357
+ watchState(store, (state) => {
1358
+ deepFreeze(state, stateKeys);
1359
+ });
1360
+ },
1361
+ })));
1362
+ }
1363
+
1364
+ const keyPath = 'ngrxToolkitKeyPath';
1365
+ const dbName = 'ngrxToolkitDb';
1366
+ const storeName = 'ngrxToolkitStore';
1367
+ const VERSION = 1;
1368
+ class IndexedDBService {
1369
+ /**
1370
+ * write to indexedDB
1371
+ * @param key
1372
+ * @param data
1373
+ */
1374
+ async setItem(key, data) {
1375
+ const db = await this.openDB();
1376
+ const tx = db.transaction(storeName, 'readwrite');
1377
+ const store = tx.objectStore(storeName);
1378
+ store.put({
1379
+ [keyPath]: key,
1380
+ value: data,
1381
+ });
1382
+ return new Promise((resolve, reject) => {
1383
+ tx.oncomplete = () => {
1384
+ db.close();
1385
+ resolve();
1386
+ };
1387
+ tx.onerror = () => {
1388
+ db.close();
1389
+ reject();
1390
+ };
1391
+ });
1392
+ }
1393
+ /**
1394
+ * read from indexedDB
1395
+ * @param key
1396
+ */
1397
+ async getItem(key) {
1398
+ const db = await this.openDB();
1399
+ const tx = db.transaction(storeName, 'readonly');
1400
+ const store = tx.objectStore(storeName);
1401
+ const request = store.get(key);
1402
+ return new Promise((resolve, reject) => {
1403
+ request.onsuccess = () => {
1404
+ db.close();
1405
+ // localStorage(sessionStorage) returns null if the key does not exist
1406
+ // Similarly, indexedDB should return null
1407
+ if (request.result === undefined) {
1408
+ resolve(null);
1409
+ }
1410
+ resolve(request.result?.['value']);
1411
+ };
1412
+ request.onerror = () => {
1413
+ db.close();
1414
+ reject();
1415
+ };
1416
+ });
1417
+ }
1418
+ /**
1419
+ * delete indexedDB
1420
+ * @param key
1421
+ */
1422
+ async clear(key) {
1423
+ const db = await this.openDB();
1424
+ const tx = db.transaction(storeName, 'readwrite');
1425
+ const store = tx.objectStore(storeName);
1426
+ const request = store.delete(key);
1427
+ return new Promise((resolve, reject) => {
1428
+ request.onsuccess = () => {
1429
+ db.close();
1430
+ resolve();
1431
+ };
1432
+ request.onerror = () => {
1433
+ db.close();
1434
+ reject();
1435
+ };
1436
+ });
1437
+ }
1438
+ /**
1439
+ * open indexedDB
1440
+ */
1441
+ async openDB() {
1442
+ return new Promise((resolve, reject) => {
1443
+ const request = indexedDB.open(dbName, VERSION);
1444
+ request.onupgradeneeded = () => {
1445
+ const db = request.result;
1446
+ if (!db.objectStoreNames.contains(storeName)) {
1447
+ db.createObjectStore(storeName, { keyPath });
1448
+ }
1449
+ };
1450
+ request.onsuccess = () => {
1451
+ resolve(request.result);
1452
+ };
1453
+ request.onerror = () => {
1454
+ reject(request.error);
1455
+ };
1456
+ });
1457
+ }
1458
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IndexedDBService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1459
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IndexedDBService, providedIn: 'root' }); }
1460
+ }
1461
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: IndexedDBService, decorators: [{
1462
+ type: Injectable,
1463
+ args: [{ providedIn: 'root' }]
1464
+ }] });
1465
+
1466
+ /**
1467
+ * AsyncFeatureResult is used as the public interface that users interact with
1468
+ * when calling `withIndexedDB`. It intentionally omits the internal SYNC_STATUS
1469
+ * property to avoid TypeScript error TS4058 (return type of public method
1470
+ * includes private type).
1471
+ *
1472
+ * For internal implementation, we use AsyncStoreForFactory which includes
1473
+ * the SYNC_STATUS property needed for state management.
1474
+ */
1475
+ const SYNC_STATUS = Symbol('SYNC_STATUS');
1476
+
1477
+ function withIndexedDB() {
1478
+ function factory({ key, parse, select, stringify }, store, useStubs) {
1479
+ if (useStubs) {
1480
+ return {
1481
+ clearStorage: () => Promise.resolve(),
1482
+ readFromStorage: () => Promise.resolve(),
1483
+ writeToStorage: () => Promise.resolve(),
1484
+ };
1485
+ }
1486
+ const indexeddbService = inject(IndexedDBService);
1487
+ function warnOnSyncing(mode) {
1488
+ if (store[SYNC_STATUS]() === 'syncing') {
1489
+ const prettyMode = mode === 'read' ? 'Reading' : 'Writing';
1490
+ console.warn(`${prettyMode} to Store (${key}) happened during an ongoing synchronization process.`, 'Please ensure that the store is not in syncing state via `store.whenSynced()`.', 'Alternatively, you can disable the autoSync by passing `autoSync: false` in the config.');
1491
+ }
1492
+ }
1493
+ return {
1494
+ /**
1495
+ * Removes the item stored in storage.
1496
+ */
1497
+ async clearStorage() {
1498
+ warnOnSyncing('write');
1499
+ store[SYNC_STATUS].set('syncing');
1500
+ patchState$1(store, {});
1501
+ await indexeddbService.clear(key);
1502
+ store[SYNC_STATUS].set('synced');
1503
+ },
1504
+ /**
1505
+ * Reads item from storage and patches the state.
1506
+ */
1507
+ async readFromStorage() {
1508
+ warnOnSyncing('read');
1509
+ store[SYNC_STATUS].set('syncing');
1510
+ const stateString = await indexeddbService.getItem(key);
1511
+ if (stateString) {
1512
+ patchState$1(store, parse(stateString));
1513
+ }
1514
+ store[SYNC_STATUS].set('synced');
1515
+ },
1516
+ /**
1517
+ * Writes selected portion to storage.
1518
+ */
1519
+ async writeToStorage() {
1520
+ warnOnSyncing('write');
1521
+ store[SYNC_STATUS].set('syncing');
1522
+ const slicedState = select(getState(store));
1523
+ await indexeddbService.setItem(key, stringify(slicedState));
1524
+ store[SYNC_STATUS].set('synced');
1525
+ },
1526
+ };
1527
+ }
1528
+ factory.type = 'async';
1529
+ return factory;
1530
+ }
1531
+
1532
+ class LocalStorageService {
1533
+ getItem(key) {
1534
+ return localStorage.getItem(key);
1535
+ }
1536
+ setItem(key, data) {
1537
+ return localStorage.setItem(key, data);
1538
+ }
1539
+ clear(key) {
1540
+ return localStorage.removeItem(key);
1541
+ }
1542
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LocalStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1543
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LocalStorageService, providedIn: 'root' }); }
1544
+ }
1545
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: LocalStorageService, decorators: [{
1546
+ type: Injectable,
1547
+ args: [{
1548
+ providedIn: 'root',
1549
+ }]
1550
+ }] });
1551
+
1552
+ class SessionStorageService {
1553
+ getItem(key) {
1554
+ return sessionStorage.getItem(key);
1555
+ }
1556
+ setItem(key, data) {
1557
+ return sessionStorage.setItem(key, data);
1558
+ }
1559
+ clear(key) {
1560
+ return sessionStorage.removeItem(key);
1561
+ }
1562
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SessionStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1563
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SessionStorageService, providedIn: 'root' }); }
1564
+ }
1565
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SessionStorageService, decorators: [{
1566
+ type: Injectable,
1567
+ args: [{
1568
+ providedIn: 'root',
1569
+ }]
1570
+ }] });
1571
+
1572
+ function withLocalStorage() {
1573
+ return createSyncMethods(LocalStorageService);
1574
+ }
1575
+ function withSessionStorage() {
1576
+ return createSyncMethods(SessionStorageService);
1577
+ }
1578
+ function createSyncMethods(Storage) {
1579
+ function factory({ key, parse, select, stringify }, store, useStubs) {
1580
+ if (useStubs) {
1581
+ return {
1582
+ clearStorage: () => undefined,
1583
+ readFromStorage: () => undefined,
1584
+ writeToStorage: () => undefined,
1585
+ };
1586
+ }
1587
+ const storage = inject(Storage);
1588
+ return {
1589
+ clearStorage() {
1590
+ storage.clear(key);
1591
+ },
1592
+ readFromStorage() {
1593
+ const stateString = storage.getItem(key);
1594
+ if (stateString) {
1595
+ patchState$1(store, parse(stateString));
1596
+ }
1597
+ },
1598
+ writeToStorage() {
1599
+ const slicedState = select(getState(store));
1600
+ storage.setItem(key, stringify(slicedState));
1601
+ },
1602
+ };
1603
+ }
1604
+ factory.type = 'sync';
1605
+ return factory;
1606
+ }
1607
+
1608
+ function withStorageSync(configOrKey, storageStrategy) {
1609
+ if (typeof configOrKey !== 'string' &&
1610
+ configOrKey.storage &&
1611
+ storageStrategy) {
1612
+ throw new Error('You can either pass a storage strategy or a config with storage, but not both.');
1613
+ }
1614
+ const config = {
1615
+ autoSync: true,
1616
+ select: (state) => state,
1617
+ parse: JSON.parse,
1618
+ stringify: JSON.stringify,
1619
+ storage: () => localStorage,
1620
+ ...(typeof configOrKey === 'string' ? { key: configOrKey } : configOrKey),
1621
+ };
1622
+ const factory = storageStrategy ??
1623
+ (config.storage() === localStorage
1624
+ ? withLocalStorage()
1625
+ : withSessionStorage());
1626
+ if (factory.type === 'sync') {
1627
+ return createSyncStorageSync(factory, config);
1628
+ }
1629
+ else {
1630
+ return createAsyncStorageSync(factory, config);
1631
+ }
1632
+ }
1633
+ function createSyncStorageSync(factory, config) {
1634
+ return signalStoreFeature(withMethods((store, platformId = inject(PLATFORM_ID)) => {
1635
+ return factory(config, store, isPlatformServer(platformId));
1636
+ }), withHooks({
1637
+ onInit(store, platformId = inject(PLATFORM_ID)) {
1638
+ if (isPlatformServer(platformId)) {
1639
+ return;
1640
+ }
1641
+ if (config.autoSync) {
1642
+ store.readFromStorage();
1643
+ watchState(store, () => store.writeToStorage());
1644
+ }
1645
+ },
1646
+ }));
1647
+ }
1648
+ function createAsyncStorageSync(factory, config) {
1649
+ return signalStoreFeature(withProps(() => {
1650
+ const props = {
1651
+ /*
1652
+ // we need to have that as property (and not state)
1653
+ // Otherwise the state watcher fires when updating the sync status
1654
+ */
1655
+ [SYNC_STATUS]: signal('idle'),
1656
+ };
1657
+ const resolves = [];
1658
+ effect(() => {
1659
+ const syncStatus = props[SYNC_STATUS]();
1660
+ if (syncStatus === 'synced') {
1661
+ resolves.forEach((resolve) => resolve());
1662
+ resolves.splice(0, resolves.length);
1663
+ }
1664
+ });
1665
+ return {
1666
+ ...props,
1667
+ isSynced: computed(() => props[SYNC_STATUS]() === 'synced'),
1668
+ whenSynced: () => new Promise((resolve) => {
1669
+ if (props[SYNC_STATUS]() === 'synced') {
1670
+ resolve();
1671
+ }
1672
+ else {
1673
+ resolves.push(resolve);
1674
+ }
1675
+ }),
1676
+ };
1677
+ }), withMethods((store, platformId = inject(PLATFORM_ID)) => {
1678
+ return factory(config, store, isPlatformServer(platformId));
1679
+ }), withHooks({
1680
+ async onInit(store, platformId = inject(PLATFORM_ID)) {
1681
+ if (isPlatformServer(platformId)) {
1682
+ return;
1683
+ }
1684
+ const initialState = getState(store);
1685
+ if (config.autoSync) {
1686
+ let startWatching = false;
1687
+ watchState(store, () => {
1688
+ if (!startWatching) {
1689
+ if (getState(store) === initialState) {
1690
+ return;
1691
+ }
1692
+ console.warn(`Writing to Store (${config.key}) happened before the state was initially read from storage.`, 'Please ensure that the store is not in syncing state via `store.whenSynced()` before writing to the state.', 'Alternatively, you can disable autoSync by passing `autoSync: false` in the config.');
1693
+ return;
1694
+ }
1695
+ return store.writeToStorage();
1696
+ });
1697
+ await store.readFromStorage();
1698
+ startWatching = true;
1699
+ }
1700
+ },
1701
+ }));
1702
+ }
1703
+
1704
+ /**
1705
+ * `withConditional` activates a feature based on a given condition.
1706
+ *
1707
+ * **Use Cases**
1708
+ * - Conditionally activate features based on the **store state** or other criteria.
1709
+ * - Choose between **two different implementations** of a feature.
1710
+ *
1711
+ * **Type Constraints**
1712
+ * Both features must have **exactly the same state, props, and methods**.
1713
+ * Otherwise, a type error will occur.
1714
+ *
1715
+ *
1716
+ * **Usage**
1717
+ *
1718
+ * ```typescript
1719
+ * const withUser = signalStoreFeature(
1720
+ * withState({ id: 1, name: 'Konrad' }),
1721
+ * withHooks(store => ({
1722
+ * onInit() {
1723
+ * // user loading logic
1724
+ * }
1725
+ * }))
1726
+ * );
1727
+ *
1728
+ * function withFakeUser() {
1729
+ * return signalStoreFeature(
1730
+ * withState({ id: 0, name: 'anonymous' })
1731
+ * );
1732
+ * }
1733
+ *
1734
+ * signalStore(
1735
+ * withMethods(() => ({
1736
+ * useRealUser: () => true
1737
+ * })),
1738
+ * withConditional((store) => store.useRealUser(), withUser, withFakeUser)
1739
+ * )
1740
+ * ```
1741
+ *
1742
+ * @param condition - A function that determines which feature to activate based on the store state.
1743
+ * @param featureIfTrue - The feature to activate if the condition evaluates to `true`.
1744
+ * @param featureIfFalse - The feature to activate if the condition evaluates to `false`.
1745
+ * @returns A `SignalStoreFeature` that applies the selected feature based on the condition.
1746
+ */
1747
+ function withConditional(condition, featureIfTrue, featureIfFalse) {
1748
+ return (store) => {
1749
+ const conditionStore = {
1750
+ ...store['stateSignals'],
1751
+ ...store['props'],
1752
+ ...store['methods'],
1753
+ };
1754
+ return condition(conditionStore)
1755
+ ? featureIfTrue(store)
1756
+ : featureIfFalse(store);
1757
+ };
1758
+ }
1759
+ const emptyFeature = signalStoreFeature(withState({}));
1760
+
1761
+ /**
1762
+ * @deprecated Use `import { withFeature } from '@ngrx/signals'` instead, starting with `ngrx/signals` 19.1: https://ngrx.io/guide/signals/signal-store/custom-store-features#connecting-a-custom-feature-with-the-store
1763
+ *
1764
+ * Allows to pass properties, methods, or signals from a SignalStore
1765
+ * to a feature.
1766
+ *
1767
+ * Typically, a `signalStoreFeature` can have input constraints on
1768
+ *
1769
+ * ```typescript
1770
+ * function withSum(a: Signal<number>, b: Signal<number>) {
1771
+ * return signalStoreFeature(
1772
+ * withComputed(() => ({
1773
+ * sum: computed(() => a() + b())
1774
+ * }))
1775
+ * );
1776
+ * }
1777
+ *
1778
+ * signalStore(
1779
+ * withState({ a: 1, b: 2 }),
1780
+ * withFeatureFactory((store) => withSum(store.a, store.b))
1781
+ * );
1782
+ * ```
1783
+ * @param factoryFn
1784
+ */
1785
+ function withFeatureFactory(factoryFn) {
1786
+ return (store) => {
1787
+ const storeForFactory = {
1788
+ ...store['stateSignals'],
1789
+ ...store['props'],
1790
+ ...store['methods'],
1791
+ };
1792
+ const feature = factoryFn(storeForFactory);
1793
+ return feature(store);
1794
+ };
1795
+ }
1796
+
1797
+ const switchOp = {
1798
+ rxJsOperator: switchMap,
1799
+ exhaustSemantics: false,
1800
+ };
1801
+ const mergeOp = {
1802
+ rxJsOperator: mergeMap,
1803
+ exhaustSemantics: false,
1804
+ };
1805
+ const concatOp = {
1806
+ rxJsOperator: concatMap,
1807
+ exhaustSemantics: false,
1808
+ };
1809
+ const exhaustOp = {
1810
+ rxJsOperator: exhaustMap,
1811
+ exhaustSemantics: true,
1812
+ };
1813
+
1814
+ /**
1815
+ * Creates a mutation that leverages RxJS.
1816
+ *
1817
+ * For each mutation the following options can be defined:
1818
+ * - `operation`: A function that defines the mutation logic. It returns an Observable.
1819
+ * - `onSuccess`: A callback that is called when the mutation is successful.
1820
+ * - `onError`: A callback that is called when the mutation fails.
1821
+ * - `operator`: An optional wrapper of an RxJS flattening operator. By default `concat` sematics are used.
1822
+ * - `injector`: An optional Angular injector to use for dependency injection.
1823
+ *
1824
+ * The `operation` is the only mandatory option.
1825
+ *
1826
+ * The returned mutation can be called as an async function and returns a Promise.
1827
+ * This promise informs about whether the mutation was successful, failed, or aborted
1828
+ * (due to switchMap or exhaustMap semantics).
1829
+ *
1830
+ * The mutation also provides several Signals such as error, status or isPending (see below).
1831
+ *
1832
+ * Example usage without Store:
1833
+ *
1834
+ * ```typescript
1835
+ * const counterSignal = signal(0);
1836
+ *
1837
+ * const increment = rxMutation({
1838
+ * operation: (param: Param) => {
1839
+ * return calcSum(this.counterSignal(), param.value);
1840
+ * },
1841
+ * operator: concatOp,
1842
+ * onSuccess: (result) => {
1843
+ * this.counterSignal.set(result);
1844
+ * },
1845
+ * onError: (error) => {
1846
+ * console.error('Error occurred:', error);
1847
+ * },
1848
+ * });
1849
+ *
1850
+ * const error = increment.error;
1851
+ * const isPending = increment.isPending;
1852
+ * const status = increment.status;
1853
+ * const value = increment.value;
1854
+ * const hasValue = increment.hasValue;
1855
+ *
1856
+ * async function incrementCounter() {
1857
+ * const result = await increment({ value: 1 });
1858
+ * if (result.status === 'success') {
1859
+ * console.log('Success:', result.value);
1860
+ * }
1861
+ * if (result.status === 'error') {
1862
+ * console.log('Error:', result.error);
1863
+ * }
1864
+ * if (result.status === 'aborted') {
1865
+ * console.log('Operation aborted');
1866
+ * }
1867
+ * }
1868
+ *
1869
+ * function calcSum(a: number, b: number): Observable<number> {
1870
+ * return of(result).pipe(delay(500));
1871
+ * }
1872
+ * ```
1873
+ *
1874
+ * @param options
1875
+ * @returns the actual mutation function along tracking data as properties/methods
1876
+ */
1877
+ function rxMutation(optionsOrOperation) {
1878
+ const inputSubject = new Subject();
1879
+ const options = typeof optionsOrOperation === 'function'
1880
+ ? { operation: optionsOrOperation }
1881
+ : optionsOrOperation;
1882
+ const flatteningOp = options.operator ?? concatOp;
1883
+ const destroyRef = options.injector?.get(DestroyRef) ?? inject(DestroyRef);
1884
+ const callCount = signal(0);
1885
+ const errorSignal = signal(undefined);
1886
+ const idle = signal(true);
1887
+ const isPending = computed(() => callCount() > 0);
1888
+ const value = signal(undefined);
1889
+ const isSuccess = computed(() => !idle() && !isPending() && !errorSignal());
1890
+ const hasValue = function () {
1891
+ return typeof value() !== 'undefined';
1892
+ };
1893
+ const status = computed(() => {
1894
+ if (idle()) {
1895
+ return 'idle';
1896
+ }
1897
+ if (callCount() > 0) {
1898
+ return 'pending';
1899
+ }
1900
+ if (errorSignal()) {
1901
+ return 'error';
1902
+ }
1903
+ return 'success';
1904
+ });
1905
+ const initialInnerStatus = 'idle';
1906
+ let innerStatus = initialInnerStatus;
1907
+ inputSubject
1908
+ .pipe(flatteningOp.rxJsOperator((input) => defer(() => {
1909
+ callCount.update((c) => c + 1);
1910
+ idle.set(false);
1911
+ return options.operation(input.param).pipe(tap((result) => {
1912
+ options.onSuccess?.(result, input.param);
1913
+ innerStatus = 'success';
1914
+ errorSignal.set(undefined);
1915
+ value.set(result);
1916
+ }), catchError((error) => {
1917
+ options.onError?.(error, input.param);
1918
+ errorSignal.set(error);
1919
+ value.set(undefined);
1920
+ innerStatus = 'error';
1921
+ return EMPTY;
1922
+ }), finalize(() => {
1923
+ callCount.update((c) => c - 1);
1924
+ if (innerStatus === 'success') {
1925
+ input.resolve({
1926
+ status: 'success',
1927
+ value: value(),
1928
+ });
1929
+ }
1930
+ else if (innerStatus === 'error') {
1931
+ input.resolve({
1932
+ status: 'error',
1933
+ error: errorSignal(),
1934
+ });
1935
+ }
1936
+ else {
1937
+ input.resolve({
1938
+ status: 'aborted',
1939
+ });
1940
+ }
1941
+ innerStatus = initialInnerStatus;
1942
+ }));
1943
+ })), takeUntilDestroyed(destroyRef))
1944
+ .subscribe();
1945
+ const mutationFn = (param) => {
1946
+ return new Promise((resolve) => {
1947
+ if (callCount() > 0 && flatteningOp.exhaustSemantics) {
1948
+ resolve({
1949
+ status: 'aborted',
1950
+ });
1951
+ }
1952
+ else {
1953
+ inputSubject.next({
1954
+ param,
1955
+ resolve,
1956
+ });
1957
+ }
1958
+ });
1959
+ };
1960
+ const mutation = mutationFn;
1961
+ mutation.status = status;
1962
+ mutation.isPending = isPending;
1963
+ mutation.error = errorSignal;
1964
+ mutation.value = value;
1965
+ mutation.hasValue = hasValue;
1966
+ mutation.isSuccess = isSuccess;
1967
+ return mutation;
1968
+ }
1969
+
1970
+ function withMutations(mutationsFactory) {
1971
+ return (store) => {
1972
+ // TODO: Is this the correct usage?
1973
+ const source = store;
1974
+ const mutations = mutationsFactory({
1975
+ ...source,
1976
+ ...store.props,
1977
+ ...store.methods,
1978
+ ...store.stateSignals,
1979
+ });
1980
+ const feature = createMutationsFeature(mutations);
1981
+ return feature(store);
1982
+ };
1983
+ }
1984
+ function createMutationsFeature(mutations) {
1985
+ const keys = Object.keys(mutations);
1986
+ const feature = signalStoreFeature(withMethods(() => keys.reduce((acc, key) => ({
1987
+ ...acc,
1988
+ [key]: async (params) => {
1989
+ const mutation = mutations[key];
1990
+ if (!mutation) {
1991
+ throw new Error(`Mutation ${key} not found`);
1992
+ }
1993
+ const result = await mutation(params);
1994
+ return result;
1995
+ },
1996
+ }), {})), withComputed(() => keys.reduce((acc, key) => ({
1997
+ ...acc,
1998
+ [`${key}IsPending`]: mutations[key].isPending,
1999
+ [`${key}Status`]: mutations[key].status,
2000
+ [`${key}Error`]: mutations[key].error,
2001
+ }), {})));
2002
+ return feature;
2003
+ }
2004
+
2005
+ /**
2006
+ * Creates an HTTP mutation.
2007
+ *
2008
+ * export type Params = {
2009
+ * value: number;
2010
+ * };
2011
+ *
2012
+ * export type CounterResponse = {
2013
+ * // httpbin.org echos the request using the
2014
+ * // json property
2015
+ * json: { counter: number };
2016
+ * };
2017
+ *
2018
+ * const simpleSaveUser = httpMutation({
2019
+ * request: (userData: AddUserEntry) => ({
2020
+ * url: 'api/users',
2021
+ * body: userData,
2022
+ * }),
2023
+ * parse: Boolean,
2024
+ * })
2025
+ *
2026
+ * const saveUser = httpMutation({
2027
+ * request: (p: Params) => ({
2028
+ * url: `https://httpbin.org/post`,
2029
+ * method: 'POST',
2030
+ * body: { counter: p.value },
2031
+ * headers: { 'Content-Type': 'application/json' },
2032
+ * }),
2033
+ * onSuccess: (response: CounterResponse) => {
2034
+ * console.log('Counter sent to server:', response);
2035
+ * },
2036
+ * onError: (error) => {
2037
+ * console.error('Failed to send counter:', error);
2038
+ * },
2039
+ * });
2040
+ *
2041
+ * ...
2042
+ *
2043
+ * const result = await this.saveUser({ value: 17 });
2044
+ * if (result.status === 'success') {
2045
+ * console.log('Successfully saved to server:', result.value);
2046
+ * }
2047
+ * else if (result.status === 'error') {
2048
+ * console.log('Failed to save:', result.error);
2049
+ * }
2050
+ * else {
2051
+ * console.log('Operation aborted');
2052
+ * }
2053
+ *
2054
+ * @param options The options for the HTTP mutation.
2055
+ * @returns The HTTP mutation.
2056
+ */
2057
+ function httpMutation(optionsOrRequest) {
2058
+ const httpClient = inject(HttpClient);
2059
+ const options = typeof optionsOrRequest === 'function'
2060
+ ? { request: optionsOrRequest }
2061
+ : optionsOrRequest;
2062
+ const parse = options.parse ?? ((raw) => raw);
2063
+ const uploadProgress = signal(undefined);
2064
+ const downloadProgress = signal(undefined);
2065
+ const headers = signal(undefined);
2066
+ const statusCode = signal(undefined);
2067
+ const mutation = rxMutation({
2068
+ ...options,
2069
+ operation: (param) => {
2070
+ const httpRequest = options.request(param);
2071
+ return defer(() => {
2072
+ uploadProgress.set(undefined);
2073
+ downloadProgress.set(undefined);
2074
+ headers.set(undefined);
2075
+ statusCode.set(undefined);
2076
+ return httpClient
2077
+ .request(httpRequest.method, httpRequest.url, {
2078
+ ...httpRequest,
2079
+ observe: 'events',
2080
+ responseType: 'json',
2081
+ })
2082
+ .pipe(tap((response) => {
2083
+ if (response.type === HttpEventType.UploadProgress) {
2084
+ uploadProgress.set(response);
2085
+ }
2086
+ else if (response.type === HttpEventType.DownloadProgress) {
2087
+ downloadProgress.set(response);
2088
+ }
2089
+ }), filter((event) => event instanceof HttpResponse), tap((response) => {
2090
+ headers.set(response.headers);
2091
+ statusCode.set(response.status.toString());
2092
+ }), map((event) => parse(event.body)));
2093
+ });
2094
+ },
2095
+ });
2096
+ mutation.uploadProgress = uploadProgress;
2097
+ mutation.downloadProgress = downloadProgress;
2098
+ mutation.statusCode = statusCode;
2099
+ mutation.headers = headers;
2100
+ return mutation;
2101
+ }
2102
+
2103
+ /**
2104
+ * Generated bundle index. Do not edit.
2105
+ */
2106
+
2107
+ export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withSessionStorage, withStorageSync, withUndoRedo };
2108
+ //# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map