@angular-architects/ngrx-toolkit 20.5.0 → 20.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, NgZone, computed, isSignal, untracked, isDevMode as isDevMode$1, DestroyRef, linkedSignal } from '@angular/core';
3
- import { watchState, getState, signalStoreFeature, withMethods, withHooks, patchState as patchState$1, withState, withComputed, withProps, withLinkedState } from '@ngrx/signals';
2
+ import { Injectable, InjectionToken, signal, effect, inject, PLATFORM_ID, NgZone, untracked, computed, isSignal, isDevMode as isDevMode$1, DestroyRef, linkedSignal } from '@angular/core';
3
+ import { watchState, getState, signalStoreFeature, withMethods, withProps, withHooks, patchState as patchState$1, type, withState, withComputed, withLinkedState } from '@ngrx/signals';
4
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
5
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
6
+ import { Dispatcher } from '@ngrx/signals/events';
7
+ import { tap, merge, Subject, switchMap, mergeMap, concatMap, exhaustMap, defer, catchError, EMPTY, finalize, filter, map } from 'rxjs';
8
+ import { removeEntity, setAllEntities, updateEntity, addEntity } from '@ngrx/signals/entities';
8
9
  import { HttpClient, HttpEventType, HttpResponse } from '@angular/common/http';
9
10
 
10
11
  const DEVTOOLS_FEATURE = Symbol('DEVTOOLS_FEATURE');
@@ -100,6 +101,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
100
101
  args: [{ providedIn: 'root' }]
101
102
  }] });
102
103
 
104
+ const GLITCH_TRACKING_FEATURE = 'GLITCH_TRACKING_FEATURE';
103
105
  /**
104
106
  * It tracks all state changes of the State, including intermediary updates
105
107
  * that are typically suppressed by Angular's glitch-free mechanism.
@@ -130,7 +132,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
130
132
  * Without `withGlitchTracking`, the DevTools would only show the final value of 3.
131
133
  */
132
134
  function withGlitchTracking() {
133
- return createDevtoolsFeature({ tracker: GlitchTrackerService });
135
+ return createDevtoolsFeature({
136
+ name: GLITCH_TRACKING_FEATURE,
137
+ tracker: GlitchTrackerService,
138
+ });
134
139
  }
135
140
 
136
141
  /**
@@ -398,7 +403,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
398
403
 
399
404
  const renameDevtoolsMethodName = '___renameDevtoolsName';
400
405
  const uniqueDevtoolsId = '___uniqueDevtoolsId';
401
- const EXISTING_NAMES = new InjectionToken('Array contain existing names for the signal stores', { factory: () => [], providedIn: 'root' });
406
+ // Used to declare the existence of the devtools extension
407
+ const DEVTOOL_FEATURE_NAMES = Symbol('DEVTOOL_PROP');
402
408
  /**
403
409
  * Adds this store as a feature state to the Redux DevTools.
404
410
  *
@@ -423,12 +429,13 @@ function withDevtools(name, ...features) {
423
429
  },
424
430
  [uniqueDevtoolsId]: () => id,
425
431
  };
426
- }), withHooks((store) => {
432
+ }), withProps(() => ({
433
+ [DEVTOOL_FEATURE_NAMES]: features.filter(Boolean).map((f) => f.name),
434
+ })), withHooks((store) => {
427
435
  const syncer = inject(DevtoolsSyncer);
428
436
  const id = String(store[uniqueDevtoolsId]());
429
437
  return {
430
438
  onInit() {
431
- const id = String(store[uniqueDevtoolsId]());
432
439
  const finalOptions = {
433
440
  indexNames: !features.some((f) => f.indexNames === false),
434
441
  map: features.find((f) => f.map)?.map ?? ((state) => state),
@@ -477,7 +484,55 @@ function updateState(stateSource, action, ...updaters) {
477
484
  /**
478
485
  * Stub for DevTools integration. Can be used to disable DevTools in production.
479
486
  */
480
- const withDevToolsStub = () => (store) => store;
487
+ const withDevToolsStub = () => signalStoreFeature(withProps(() => ({
488
+ [DEVTOOL_FEATURE_NAMES]: [GLITCH_TRACKING_FEATURE],
489
+ })));
490
+
491
+ function withTrackedReducer(
492
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
493
+ ...caseReducers) {
494
+ return signalStoreFeature({
495
+ state: type(),
496
+ }, withHooks((store) => ({
497
+ onInit() {
498
+ if (!(DEVTOOL_FEATURE_NAMES in store)) {
499
+ throw new Error(`In order to use withTrackedReducer, you must first enable the devtools feature via withDevtools('[your store name]', withGlitchTracking())`);
500
+ }
501
+ if (!store[DEVTOOL_FEATURE_NAMES].includes(GLITCH_TRACKING_FEATURE)) {
502
+ throw new Error(`In order to use withTrackedReducer, you must first enable the glitch tracking devtools feature via withDevtools('[your store name]', withGlitchTracking())`);
503
+ }
504
+ const events = getReducerEvents();
505
+ const updates = caseReducers.map((caseReducer) => events.on(...caseReducer.events).pipe(tap((event) => {
506
+ const state = untracked(() => getState(store));
507
+ const result = caseReducer.reducer(event, state);
508
+ const updaters = Array.isArray(result) ? result : [result];
509
+ updateState(store, event.type, ...updaters);
510
+ })));
511
+ merge(...updates)
512
+ .pipe(takeUntilDestroyed())
513
+ .subscribe();
514
+ },
515
+ })));
516
+ }
517
+ /**
518
+ * Returns the synchronous reducer event stream exposed by the dispatcher.
519
+ *
520
+ * NgRx's `Dispatcher` delivers events to `ReducerEvents` immediately but feeds
521
+ * the public `Events` stream via `queueScheduler`, which keeps work in a FIFO
522
+ * queue and executes scheduled tasks only after the current task completes
523
+ * ([rxjs.dev](https://rxjs.dev/api/index/const/queueScheduler)). When
524
+ * `GlitchTrackerService` captures the state change synchronously, that queued
525
+ * `Events` emission is processed afterward and DevTools records the update as
526
+ * `Store Update`. Tapping into the reducer stream keeps event names and state
527
+ * changes aligned.
528
+ *
529
+ * TODO(@ngrx): expose a synchronous events API (similar to what `withReducer` uses)
530
+ * so consumers do not need to reach into dispatcher internals.
531
+ */
532
+ function getReducerEvents() {
533
+ const dispatcher = inject(Dispatcher);
534
+ return dispatcher.reducerEvents;
535
+ }
481
536
 
482
537
  function assertActionFnSpecs(obj) {
483
538
  if (!obj || typeof obj !== 'object') {
@@ -677,6 +732,24 @@ function withRedux(redux) {
677
732
  };
678
733
  }
679
734
 
735
+ function clearUndoRedo(store, opts) {
736
+ if (canClearUndoRedo(store)) {
737
+ store.__clearUndoRedo__(opts);
738
+ }
739
+ else {
740
+ throw new Error('Cannot clear undoRedo, since store is not configured with withUndoRedo()');
741
+ }
742
+ }
743
+ function canClearUndoRedo(store) {
744
+ if ('__clearUndoRedo__' in store &&
745
+ typeof store.__clearUndoRedo__ === 'function') {
746
+ return true;
747
+ }
748
+ else {
749
+ return false;
750
+ }
751
+ }
752
+
680
753
  function deriveCallStateKeys(collection) {
681
754
  return {
682
755
  callStateKey: collection ? `${collection}CallState` : 'callState',
@@ -978,6 +1051,129 @@ function withDataService(options) {
978
1051
  }));
979
1052
  }
980
1053
 
1054
+ const defaultOptions$1 = {
1055
+ maxStackSize: 100,
1056
+ keys: [],
1057
+ skip: 0,
1058
+ };
1059
+ function getUndoRedoKeys(collections) {
1060
+ if (collections) {
1061
+ return collections.flatMap((c) => [
1062
+ `${c}EntityMap`,
1063
+ `${c}Ids`,
1064
+ `selected${capitalize(c)}Ids`,
1065
+ `${c}Filter`,
1066
+ ]);
1067
+ }
1068
+ return ['entityMap', 'ids', 'selectedIds', 'filter'];
1069
+ }
1070
+ function withUndoRedo(options) {
1071
+ let lastRecord = null;
1072
+ let skipOnce = false;
1073
+ const normalized = {
1074
+ ...defaultOptions$1,
1075
+ ...options,
1076
+ };
1077
+ //
1078
+ // Design Decision: This feature has its own
1079
+ // internal state.
1080
+ //
1081
+ const undoStack = [];
1082
+ const redoStack = [];
1083
+ const canUndo = signal(false, ...(ngDevMode ? [{ debugName: "canUndo" }] : []));
1084
+ const canRedo = signal(false, ...(ngDevMode ? [{ debugName: "canRedo" }] : []));
1085
+ const updateInternal = () => {
1086
+ canUndo.set(undoStack.length !== 0);
1087
+ canRedo.set(redoStack.length !== 0);
1088
+ };
1089
+ const keys = [...getUndoRedoKeys(normalized.collections), ...normalized.keys];
1090
+ return signalStoreFeature(withComputed(() => ({
1091
+ canUndo: canUndo.asReadonly(),
1092
+ canRedo: canRedo.asReadonly(),
1093
+ })), withMethods((store) => ({
1094
+ undo() {
1095
+ const item = undoStack.pop();
1096
+ if (item && lastRecord) {
1097
+ redoStack.push(lastRecord);
1098
+ }
1099
+ if (item) {
1100
+ skipOnce = true;
1101
+ patchState$1(store, item);
1102
+ lastRecord = item;
1103
+ }
1104
+ updateInternal();
1105
+ },
1106
+ redo() {
1107
+ const item = redoStack.pop();
1108
+ if (item && lastRecord) {
1109
+ undoStack.push(lastRecord);
1110
+ }
1111
+ if (item) {
1112
+ skipOnce = true;
1113
+ patchState$1(store, item);
1114
+ lastRecord = item;
1115
+ }
1116
+ updateInternal();
1117
+ },
1118
+ __clearUndoRedo__(opts) {
1119
+ undoStack.splice(0);
1120
+ redoStack.splice(0);
1121
+ if (opts) {
1122
+ lastRecord = opts.lastRecord;
1123
+ }
1124
+ updateInternal();
1125
+ },
1126
+ })), withMethods((store) => ({
1127
+ /** @deprecated Use {@link clearUndoRedo} instead. */
1128
+ clearStack() {
1129
+ store.__clearUndoRedo__();
1130
+ },
1131
+ })), withHooks({
1132
+ onInit(store) {
1133
+ watchState(store, () => {
1134
+ const cand = keys.reduce((acc, key) => {
1135
+ const s = store[key];
1136
+ if (s && isSignal(s)) {
1137
+ return {
1138
+ ...acc,
1139
+ [key]: s(),
1140
+ };
1141
+ }
1142
+ return acc;
1143
+ }, {});
1144
+ if (normalized.skip > 0) {
1145
+ normalized.skip--;
1146
+ return;
1147
+ }
1148
+ if (skipOnce) {
1149
+ skipOnce = false;
1150
+ return;
1151
+ }
1152
+ //
1153
+ // Deep Comparison to prevent duplicated entries
1154
+ // on the stack. This can e.g. happen after an undo
1155
+ // if the component sends back the undone filter
1156
+ // to the store.
1157
+ //
1158
+ if (JSON.stringify(cand) === JSON.stringify(lastRecord)) {
1159
+ return;
1160
+ }
1161
+ // Clear redoStack after recorded action
1162
+ redoStack.splice(0);
1163
+ if (lastRecord) {
1164
+ undoStack.push(lastRecord);
1165
+ }
1166
+ if (redoStack.length > normalized.maxStackSize) {
1167
+ undoStack.unshift();
1168
+ }
1169
+ lastRecord = cand;
1170
+ // Don't propogate current reactive context
1171
+ untracked(() => updateInternal());
1172
+ });
1173
+ },
1174
+ }));
1175
+ }
1176
+
981
1177
  /** 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
1178
  * 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
1179
  * 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
@@ -1174,122 +1370,6 @@ function setResetState(store, state) {
1174
1370
  store.__setResetState__(state);
1175
1371
  }
1176
1372
 
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, ...(ngDevMode ? [{ debugName: "canUndo" }] : []));
1207
- const canRedo = signal(false, ...(ngDevMode ? [{ debugName: "canRedo" }] : []));
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
- watchState(store, () => {
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
1373
  /**
1294
1374
  * Deep freezes a state object along its properties with primitive values
1295
1375
  * on the first level.
@@ -1970,7 +2050,14 @@ function rxMutation(optionsOrOperation) {
1970
2050
  }
1971
2051
 
1972
2052
  //** Types for `withResource` */
1973
- function withResource(resourceFactory) {
2053
+ const defaultOptions = {
2054
+ errorHandling: 'undefined value',
2055
+ };
2056
+ function withResource(resourceFactory, resourceOptions) {
2057
+ const options = {
2058
+ ...defaultOptions,
2059
+ ...(resourceOptions || {}),
2060
+ };
1974
2061
  return (store) => {
1975
2062
  const resourceOrDictionary = resourceFactory({
1976
2063
  ...store.stateSignals,
@@ -1978,18 +2065,20 @@ function withResource(resourceFactory) {
1978
2065
  ...store.methods,
1979
2066
  });
1980
2067
  if (isResourceRef(resourceOrDictionary)) {
1981
- return createUnnamedResource(resourceOrDictionary)(store);
2068
+ return createUnnamedResource(resourceOrDictionary, options.errorHandling)(store);
1982
2069
  }
1983
2070
  else {
1984
- return createNamedResource(resourceOrDictionary)(store);
2071
+ return createNamedResource(resourceOrDictionary, options.errorHandling)(store);
1985
2072
  }
1986
2073
  };
1987
2074
  }
1988
- function createUnnamedResource(resource) {
2075
+ function createUnnamedResource(resource, errorHandling) {
1989
2076
  function hasValue() {
1990
2077
  return resource.hasValue();
1991
2078
  }
1992
- return signalStoreFeature(withLinkedState(() => ({ value: resource.value })), withProps(() => ({
2079
+ return signalStoreFeature(withLinkedState(() => ({
2080
+ value: valueSignalForErrorHandling(resource, errorHandling),
2081
+ })), withProps(() => ({
1993
2082
  status: resource.status,
1994
2083
  error: resource.error,
1995
2084
  isLoading: resource.isLoading,
@@ -1998,11 +2087,11 @@ function createUnnamedResource(resource) {
1998
2087
  _reload: () => resource.reload(),
1999
2088
  })));
2000
2089
  }
2001
- function createNamedResource(dictionary) {
2090
+ function createNamedResource(dictionary, errorHandling) {
2002
2091
  const keys = Object.keys(dictionary);
2003
2092
  const state = keys.reduce((state, resourceName) => ({
2004
2093
  ...state,
2005
- [`${resourceName}Value`]: dictionary[resourceName].value,
2094
+ [`${resourceName}Value`]: valueSignalForErrorHandling(dictionary[resourceName], errorHandling),
2006
2095
  }), {});
2007
2096
  const props = keys.reduce((props, resourceName) => ({
2008
2097
  ...props,
@@ -2067,6 +2156,65 @@ function mapToResource(store, name) {
2067
2156
  hasValue,
2068
2157
  };
2069
2158
  }
2159
+ function valueSignalForErrorHandling(res, errorHandling) {
2160
+ const originalSignal = res.value;
2161
+ switch (errorHandling) {
2162
+ case 'native':
2163
+ return originalSignal;
2164
+ case 'undefined value': {
2165
+ return new Proxy(originalSignal, {
2166
+ apply(target) {
2167
+ const status = untracked(() => res.status());
2168
+ try {
2169
+ // Always call the underlying signal to ensure reactivity.
2170
+ const value = target();
2171
+ if (status === 'error') {
2172
+ return undefined;
2173
+ }
2174
+ return value;
2175
+ }
2176
+ catch (error) {
2177
+ if (status === 'error') {
2178
+ return undefined;
2179
+ }
2180
+ throw error;
2181
+ }
2182
+ },
2183
+ });
2184
+ }
2185
+ case 'previous value': {
2186
+ let previousValue = undefined;
2187
+ let hasPreviousValue = false;
2188
+ return new Proxy(originalSignal, {
2189
+ apply(target) {
2190
+ const status = untracked(() => res.status());
2191
+ try {
2192
+ // Always call the underlying signal to ensure reactivity.
2193
+ const value = target();
2194
+ if (status === 'error') {
2195
+ if (!hasPreviousValue) {
2196
+ throw new Error('Impossible state: previous value is not available -> resource was initialized with error');
2197
+ }
2198
+ return previousValue;
2199
+ }
2200
+ previousValue = value;
2201
+ hasPreviousValue = true;
2202
+ return value;
2203
+ }
2204
+ catch (error) {
2205
+ if (status === 'error') {
2206
+ if (!hasPreviousValue) {
2207
+ throw new Error('Impossible state: previous value is not available -> resource was initialized with error');
2208
+ }
2209
+ return previousValue;
2210
+ }
2211
+ throw error;
2212
+ }
2213
+ },
2214
+ });
2215
+ }
2216
+ }
2217
+ }
2070
2218
 
2071
2219
  function withEntityResources(entityResourceFactory) {
2072
2220
  return (store) => {
@@ -2081,38 +2229,67 @@ function withEntityResources(entityResourceFactory) {
2081
2229
  return createNamedEntityResources(resourceOrDict)(store);
2082
2230
  };
2083
2231
  }
2232
+ /**
2233
+ * We cannot use the value of `resource` directly, but
2234
+ * have to use the one created through {@link withResource}
2235
+ * because {@link withResource} creates a Proxy around the resource value
2236
+ * to avoid the error throwing behavior of the Resource API.
2237
+ */
2084
2238
  function createUnnamedEntityResource(resource) {
2085
- const { idsLinked, entityMapLinked, entitiesSignal } = createEntityDerivations(resource.value);
2086
- return signalStoreFeature(withResource(() => resource), withLinkedState(() => ({
2087
- entityMap: entityMapLinked,
2088
- ids: idsLinked,
2089
- })), withComputed(() => ({
2090
- entities: entitiesSignal,
2239
+ return signalStoreFeature(withResource(() => resource), withLinkedState(({ value }) => {
2240
+ const { ids, entityMap } = createEntityDerivations(value);
2241
+ return {
2242
+ entityMap,
2243
+ ids,
2244
+ };
2245
+ }), withComputed(({ ids, entityMap }) => ({
2246
+ entities: createComputedEntities(ids, entityMap),
2091
2247
  })));
2092
2248
  }
2249
+ /**
2250
+ * See {@link createUnnamedEntityResource} for why we cannot use the value of `resource` directly.
2251
+ */
2093
2252
  function createNamedEntityResources(dictionary) {
2094
2253
  const keys = Object.keys(dictionary);
2095
- const linkedState = {};
2096
- const computedProps = {};
2097
- keys.forEach((name) => {
2098
- const ref = dictionary[name];
2099
- const { idsLinked, entityMapLinked, entitiesSignal } = createEntityDerivations(ref.value);
2100
- linkedState[`${String(name)}EntityMap`] = entityMapLinked;
2101
- linkedState[`${String(name)}Ids`] = idsLinked;
2102
- computedProps[`${String(name)}Entities`] = entitiesSignal;
2254
+ const stateFactories = keys.map((name) => {
2255
+ return (store) => {
2256
+ const resourceValue = store[`${name}Value`];
2257
+ if (!isSignal(resourceValue)) {
2258
+ throw new Error(`Resource's value ${name}Value does not exist`);
2259
+ }
2260
+ const { ids, entityMap } = createEntityDerivations(resourceValue);
2261
+ return {
2262
+ [`${name}EntityMap`]: entityMap,
2263
+ [`${name}Ids`]: ids,
2264
+ };
2265
+ };
2103
2266
  });
2104
- return signalStoreFeature(withResource(() => dictionary), withLinkedState(() => linkedState), withComputed(() => computedProps));
2267
+ const computedFactories = keys.map((name) => {
2268
+ return (store) => {
2269
+ const ids = store[`${name}Ids`];
2270
+ const entityMap = store[`${name}EntityMap`];
2271
+ if (!isSignal(ids)) {
2272
+ throw new Error(`Entity Resource's ids ${name}Ids does not exist`);
2273
+ }
2274
+ if (!isSignal(entityMap)) {
2275
+ throw new Error(`Entity Resource's entityMap ${name}EntityMap does not exist`);
2276
+ }
2277
+ return {
2278
+ [`${name}Entities`]: createComputedEntities(ids, entityMap),
2279
+ };
2280
+ };
2281
+ });
2282
+ return signalStoreFeature(withResource(() => dictionary), withLinkedState((store) => stateFactories.reduce((acc, factory) => ({ ...acc, ...factory(store) }), {})), withComputed((store) => computedFactories.reduce((acc, factory) => ({ ...acc, ...factory(store) }), {})));
2105
2283
  }
2106
2284
  /**
2107
2285
  * @internal
2108
2286
  * @description
2109
2287
  *
2110
- * Creates the three entity-related signals (`ids`, `entityMap`, `entities`) from
2288
+ * Creates the two entity-related state properties (`ids`, `entityMap`) from
2111
2289
  * a single source signal of entities. This mirrors the public contract of
2112
2290
  * `withEntities()`:
2113
2291
  * - `ids`: derived list of entity ids
2114
2292
  * - `entityMap`: map of id -> entity
2115
- * - `entities`: projection of `ids` through `entityMap`
2116
2293
  *
2117
2294
  * Implementation details:
2118
2295
  * - Uses `withLinkedState` + `linkedSignal` for `ids` and `entityMap` so they are
@@ -2130,11 +2307,11 @@ function createNamedEntityResources(dictionary) {
2130
2307
  * and avoids imperative syncing code.
2131
2308
  */
2132
2309
  function createEntityDerivations(source) {
2133
- const idsLinked = linkedSignal({
2310
+ const ids = linkedSignal({
2134
2311
  source,
2135
2312
  computation: (list) => (list ?? []).map((e) => e.id),
2136
2313
  });
2137
- const entityMapLinked = linkedSignal({
2314
+ const entityMap = linkedSignal({
2138
2315
  source,
2139
2316
  computation: (list) => {
2140
2317
  const map = {};
@@ -2144,12 +2321,12 @@ function createEntityDerivations(source) {
2144
2321
  return map;
2145
2322
  },
2146
2323
  });
2147
- const entitiesSignal = computed(() => {
2148
- const ids = idsLinked();
2149
- const map = entityMapLinked();
2150
- return ids.map((id) => map[id]);
2151
- }, ...(ngDevMode ? [{ debugName: "entitiesSignal" }] : []));
2152
- return { idsLinked, entityMapLinked, entitiesSignal };
2324
+ return { ids, entityMap };
2325
+ }
2326
+ function createComputedEntities(ids, entityMap) {
2327
+ return () => {
2328
+ return ids().map((id) => entityMap()[id]);
2329
+ };
2153
2330
  }
2154
2331
 
2155
2332
  function withMutations(mutationsFactory) {
@@ -2289,5 +2466,5 @@ function httpMutation(optionsOrRequest) {
2289
2466
  * Generated bundle index. Do not edit.
2290
2467
  */
2291
2468
 
2292
- export { capitalize, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mapToResource, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withEntityResources, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withResource, withSessionStorage, withStorageSync, withUndoRedo };
2469
+ export { capitalize, clearUndoRedo, concatOp, createEffects, createPageArray, createReducer, deriveCallStateKeys, emptyFeature, exhaustOp, firstPage, getCallStateKeys, getCollectionArray, getDataServiceKeys, getUndoRedoKeys, gotoPage, httpMutation, mapToResource, mergeOp, nextPage, noPayload, patchState, payload, previousPage, provideDevtoolsConfig, renameDevtoolsName, rxMutation, setError, setLoaded, setLoading, setMaxPageNavigationArrayItems, setPageSize, setResetState, switchOp, updateState, withCallState, withConditional, withDataService, withDevToolsStub, withDevtools, withDisabledNameIndices, withEntityResources, withFeatureFactory, withGlitchTracking, withImmutableState, withIndexedDB, withIndexedDB as withIndexeddb, withLocalStorage, withMapper, withMutations, withPagination, withRedux, withReset, withResource, withSessionStorage, withStorageSync, withTrackedReducer, withUndoRedo };
2293
2470
  //# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map