@angular-architects/ngrx-toolkit 20.6.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,
|
|
3
|
-
import { watchState, getState, signalStoreFeature, withMethods, withHooks, patchState as patchState$1, withState, withComputed,
|
|
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({
|
|
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
|
-
|
|
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
|
-
}),
|
|
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 = () => (
|
|
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
|
-
|
|
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(() => ({
|
|
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]
|
|
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
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
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
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
2310
|
+
const ids = linkedSignal({
|
|
2134
2311
|
source,
|
|
2135
2312
|
computation: (list) => (list ?? []).map((e) => e.id),
|
|
2136
2313
|
});
|
|
2137
|
-
const
|
|
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
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
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
|