@angular-architects/ngrx-toolkit 0.1.0 → 0.1.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.
- package/esm2022/angular-architects-ngrx-toolkit.mjs +5 -0
- package/esm2022/index.mjs +9 -0
- package/esm2022/lib/assertions/assertions.mjs +6 -0
- package/esm2022/lib/redux-connector/create-redux.mjs +41 -0
- package/esm2022/lib/redux-connector/index.mjs +2 -0
- package/esm2022/lib/redux-connector/model.mjs +2 -0
- package/esm2022/lib/redux-connector/rxjs-interop/index.mjs +2 -0
- package/esm2022/lib/redux-connector/rxjs-interop/redux-method.mjs +22 -0
- package/esm2022/lib/redux-connector/signal-redux-store.mjs +43 -0
- package/esm2022/lib/redux-connector/util.mjs +13 -0
- package/esm2022/lib/shared/empty.mjs +2 -0
- package/esm2022/lib/with-call-state.mjs +58 -0
- package/esm2022/lib/with-data-service.mjs +161 -0
- package/esm2022/lib/with-devtools.mjs +79 -0
- package/esm2022/lib/with-redux.mjs +95 -0
- package/esm2022/lib/with-storage-sync.mjs +56 -0
- package/esm2022/lib/with-undo-redo.mjs +93 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +653 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
- package/{src/index.ts → index.d.ts} +2 -3
- package/lib/assertions/assertions.d.ts +2 -0
- package/lib/redux-connector/create-redux.d.ts +13 -0
- package/{src/lib/redux-connector/index.ts → lib/redux-connector/index.d.ts} +0 -1
- package/lib/redux-connector/model.d.ts +36 -0
- package/{src/lib/redux-connector/rxjs-interop/index.ts → lib/redux-connector/rxjs-interop/index.d.ts} +0 -1
- package/lib/redux-connector/rxjs-interop/redux-method.d.ts +11 -0
- package/lib/redux-connector/signal-redux-store.d.ts +11 -0
- package/lib/redux-connector/util.d.ts +5 -0
- package/lib/shared/empty.d.ts +1 -0
- package/lib/with-call-state.d.ts +56 -0
- package/lib/with-data-service.d.ts +115 -0
- package/lib/with-devtools.d.ts +32 -0
- package/lib/with-redux.d.ts +57 -0
- package/lib/with-storage-sync.d.ts +58 -0
- package/lib/with-undo-redo.d.ts +55 -0
- package/package.json +16 -3
- package/.eslintrc.json +0 -43
- package/jest.config.ts +0 -22
- package/ng-package.json +0 -7
- package/project.json +0 -37
- package/src/lib/assertions/assertions.ts +0 -9
- package/src/lib/redux-connector/create-redux.ts +0 -94
- package/src/lib/redux-connector/model.ts +0 -67
- package/src/lib/redux-connector/rxjs-interop/redux-method.ts +0 -61
- package/src/lib/redux-connector/signal-redux-store.ts +0 -54
- package/src/lib/redux-connector/util.ts +0 -22
- package/src/lib/shared/empty.ts +0 -2
- package/src/lib/with-call-state.spec.ts +0 -24
- package/src/lib/with-call-state.ts +0 -136
- package/src/lib/with-data-service.ts +0 -312
- package/src/lib/with-devtools.spec.ts +0 -157
- package/src/lib/with-devtools.ts +0 -128
- package/src/lib/with-redux.spec.ts +0 -100
- package/src/lib/with-redux.ts +0 -261
- package/src/lib/with-storage-sync.spec.ts +0 -237
- package/src/lib/with-storage-sync.ts +0 -160
- package/src/lib/with-undo-redo.ts +0 -184
- package/src/test-setup.ts +0 -8
- package/tsconfig.json +0 -29
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -16
|
@@ -0,0 +1,653 @@
|
|
|
1
|
+
import { patchState as patchState$1, signalStoreFeature, withState, withComputed, withMethods, withHooks, getState } from '@ngrx/signals';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { signal, effect, inject, PLATFORM_ID, computed, isSignal, untracked, Injectable, makeEnvironmentProviders, ENVIRONMENT_INITIALIZER, Injector } from '@angular/core';
|
|
4
|
+
import { isPlatformServer } from '@angular/common';
|
|
5
|
+
import { Subject, pipe, tap, map } from 'rxjs';
|
|
6
|
+
import { setAllEntities, addEntity, updateEntity, removeEntity } from '@ngrx/signals/entities';
|
|
7
|
+
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
|
8
|
+
|
|
9
|
+
const storeRegistry = signal({});
|
|
10
|
+
let currentActionNames = new Set();
|
|
11
|
+
let synchronizationInitialized = false;
|
|
12
|
+
function initSynchronization() {
|
|
13
|
+
effect(() => {
|
|
14
|
+
if (!connection) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const stores = storeRegistry();
|
|
18
|
+
const rootState = {};
|
|
19
|
+
for (const name in stores) {
|
|
20
|
+
const store = stores[name];
|
|
21
|
+
rootState[name] = store();
|
|
22
|
+
}
|
|
23
|
+
const names = Array.from(currentActionNames);
|
|
24
|
+
const type = names.length ? names.join(', ') : 'Store Update';
|
|
25
|
+
currentActionNames = new Set();
|
|
26
|
+
connection.send({ type }, rootState);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function getValueFromSymbol(obj, symbol) {
|
|
30
|
+
if (typeof obj === 'object' && obj && symbol in obj) {
|
|
31
|
+
return obj[symbol];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getStoreSignal(store) {
|
|
35
|
+
const [signalStateKey] = Object.getOwnPropertySymbols(store);
|
|
36
|
+
if (!signalStateKey) {
|
|
37
|
+
throw new Error('Cannot find State Signal');
|
|
38
|
+
}
|
|
39
|
+
return getValueFromSymbol(store, signalStateKey);
|
|
40
|
+
}
|
|
41
|
+
let connection;
|
|
42
|
+
/**
|
|
43
|
+
* required for testing. is not exported during build
|
|
44
|
+
*/
|
|
45
|
+
function reset() {
|
|
46
|
+
connection = undefined;
|
|
47
|
+
synchronizationInitialized = false;
|
|
48
|
+
storeRegistry.set({});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @param name store's name as it should appear in the DevTools
|
|
52
|
+
*/
|
|
53
|
+
function withDevtools(name) {
|
|
54
|
+
return (store) => {
|
|
55
|
+
const isServer = isPlatformServer(inject(PLATFORM_ID));
|
|
56
|
+
if (isServer) {
|
|
57
|
+
return store;
|
|
58
|
+
}
|
|
59
|
+
const extensions = window.__REDUX_DEVTOOLS_EXTENSION__;
|
|
60
|
+
if (!extensions) {
|
|
61
|
+
return store;
|
|
62
|
+
}
|
|
63
|
+
if (!connection) {
|
|
64
|
+
connection = extensions.connect({
|
|
65
|
+
name: 'NgRx Signal Store',
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const storeSignal = getStoreSignal(store);
|
|
69
|
+
storeRegistry.update((value) => ({
|
|
70
|
+
...value,
|
|
71
|
+
[name]: storeSignal,
|
|
72
|
+
}));
|
|
73
|
+
if (!synchronizationInitialized) {
|
|
74
|
+
initSynchronization();
|
|
75
|
+
synchronizationInitialized = true;
|
|
76
|
+
}
|
|
77
|
+
return store;
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const patchState = (state, action, ...rest) => {
|
|
81
|
+
currentActionNames.add(action);
|
|
82
|
+
return patchState$1(state, ...rest);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
function assertActionFnSpecs(obj) {
|
|
86
|
+
if (!obj || typeof obj !== 'object') {
|
|
87
|
+
throw new Error('%o is not an Action Specification');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function payload() {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
const noPayload = {};
|
|
95
|
+
function createActionFns(actionFnSpecs, reducerRegistry, effectsRegistry, state) {
|
|
96
|
+
const actionFns = {};
|
|
97
|
+
for (const type in actionFnSpecs) {
|
|
98
|
+
const actionFn = (payload) => {
|
|
99
|
+
const fullPayload = { ...payload, type };
|
|
100
|
+
const reducer = reducerRegistry[type];
|
|
101
|
+
if (reducer) {
|
|
102
|
+
reducer(state, fullPayload);
|
|
103
|
+
}
|
|
104
|
+
const effectSubject = effectsRegistry[type];
|
|
105
|
+
if (effectSubject) {
|
|
106
|
+
effectSubject.next(fullPayload);
|
|
107
|
+
}
|
|
108
|
+
return fullPayload;
|
|
109
|
+
};
|
|
110
|
+
actionFn.type = type.toString();
|
|
111
|
+
actionFns[type] = actionFn;
|
|
112
|
+
}
|
|
113
|
+
return actionFns;
|
|
114
|
+
}
|
|
115
|
+
function createPublicAndAllActionsFns(actionFnSpecs, reducerRegistry, effectsRegistry, state) {
|
|
116
|
+
if ('public' in actionFnSpecs || 'private' in actionFnSpecs) {
|
|
117
|
+
const privates = actionFnSpecs['private'] || {};
|
|
118
|
+
const publics = actionFnSpecs['public'] || {};
|
|
119
|
+
assertActionFnSpecs(privates);
|
|
120
|
+
assertActionFnSpecs(publics);
|
|
121
|
+
const privateActionFns = createActionFns(privates, reducerRegistry, effectsRegistry, state);
|
|
122
|
+
const publicActionFns = createActionFns(publics, reducerRegistry, effectsRegistry, state);
|
|
123
|
+
return {
|
|
124
|
+
all: { ...privateActionFns, ...publicActionFns },
|
|
125
|
+
publics: publicActionFns,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
const actionFns = createActionFns(actionFnSpecs, reducerRegistry, effectsRegistry, state);
|
|
129
|
+
return { all: actionFns, publics: actionFns };
|
|
130
|
+
}
|
|
131
|
+
function fillReducerRegistry(reducer, actionFns, reducerRegistry) {
|
|
132
|
+
function on(action, reducerFn) {
|
|
133
|
+
reducerRegistry[action.type] = reducerFn;
|
|
134
|
+
}
|
|
135
|
+
reducer(actionFns, on);
|
|
136
|
+
return reducerRegistry;
|
|
137
|
+
}
|
|
138
|
+
function fillEffects(effects, actionFns, effectsRegistry = {}) {
|
|
139
|
+
function create(action) {
|
|
140
|
+
const subject = new Subject();
|
|
141
|
+
effectsRegistry[action.type] = subject;
|
|
142
|
+
return subject.asObservable();
|
|
143
|
+
}
|
|
144
|
+
const effectObservables = effects(actionFns, create);
|
|
145
|
+
return Object.values(effectObservables);
|
|
146
|
+
}
|
|
147
|
+
function startSubscriptions(observables) {
|
|
148
|
+
return observables.map((observable) => observable.subscribe());
|
|
149
|
+
}
|
|
150
|
+
function processRedux(actionFnSpecs, reducer, effects, store) {
|
|
151
|
+
const reducerRegistry = {};
|
|
152
|
+
const effectsRegistry = {};
|
|
153
|
+
const actionsMap = createPublicAndAllActionsFns(actionFnSpecs, reducerRegistry, effectsRegistry, store);
|
|
154
|
+
const actionFns = actionsMap.all;
|
|
155
|
+
const publicActionsFns = actionsMap.publics;
|
|
156
|
+
fillReducerRegistry(reducer, actionFns, reducerRegistry);
|
|
157
|
+
const effectObservables = fillEffects(effects, actionFns, effectsRegistry);
|
|
158
|
+
const subscriptions = startSubscriptions(effectObservables);
|
|
159
|
+
return {
|
|
160
|
+
methods: publicActionsFns,
|
|
161
|
+
subscriptions: subscriptions,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* @param redux redux
|
|
166
|
+
*
|
|
167
|
+
* properties do not start with `with` since they are not extension functions on their own.
|
|
168
|
+
*
|
|
169
|
+
* no dependency to NgRx
|
|
170
|
+
*
|
|
171
|
+
* actions are passed to reducer and effects, but it is also possible to use other actions.
|
|
172
|
+
* effects provide forAction and do not return anything. that is important because effects should stay inaccessible
|
|
173
|
+
*/
|
|
174
|
+
function withRedux(redux) {
|
|
175
|
+
return (store) => {
|
|
176
|
+
const { methods, subscriptions } = processRedux(redux.actions, redux.reducer, redux.effects, store);
|
|
177
|
+
return {
|
|
178
|
+
...store,
|
|
179
|
+
methods,
|
|
180
|
+
};
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getCallStateKeys(config) {
|
|
185
|
+
const prop = config?.collection;
|
|
186
|
+
return {
|
|
187
|
+
callStateKey: prop ? `${config.collection}CallState` : 'callState',
|
|
188
|
+
loadingKey: prop ? `${config.collection}Loading` : 'loading',
|
|
189
|
+
loadedKey: prop ? `${config.collection}Loaded` : 'loaded',
|
|
190
|
+
errorKey: prop ? `${config.collection}Error` : 'error',
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function withCallState(config) {
|
|
194
|
+
const { callStateKey, errorKey, loadedKey, loadingKey } = getCallStateKeys(config);
|
|
195
|
+
return signalStoreFeature(withState({ [callStateKey]: 'init' }), withComputed((state) => {
|
|
196
|
+
const callState = state[callStateKey];
|
|
197
|
+
return {
|
|
198
|
+
[loadingKey]: computed(() => callState() === 'loading'),
|
|
199
|
+
[loadedKey]: computed(() => callState() === 'loaded'),
|
|
200
|
+
[errorKey]: computed(() => {
|
|
201
|
+
const v = callState();
|
|
202
|
+
return typeof v === 'object' ? v.error : null;
|
|
203
|
+
})
|
|
204
|
+
};
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
function setLoading(prop) {
|
|
208
|
+
if (prop) {
|
|
209
|
+
return { [`${prop}CallState`]: 'loading' };
|
|
210
|
+
}
|
|
211
|
+
return { callState: 'loading' };
|
|
212
|
+
}
|
|
213
|
+
function setLoaded(prop) {
|
|
214
|
+
if (prop) {
|
|
215
|
+
return { [`${prop}CallState`]: 'loaded' };
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
return { callState: 'loaded' };
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function setError(error, prop) {
|
|
222
|
+
let errorMessage = '';
|
|
223
|
+
if (!error) {
|
|
224
|
+
errorMessage = '';
|
|
225
|
+
}
|
|
226
|
+
else if (typeof error === 'object' && 'message' in error) {
|
|
227
|
+
errorMessage = String(error.message);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
errorMessage = String(error);
|
|
231
|
+
}
|
|
232
|
+
if (prop) {
|
|
233
|
+
return { [`${prop}CallState`]: { error: errorMessage } };
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
return { callState: { error: errorMessage } };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function capitalize$1(str) {
|
|
241
|
+
return str ? str[0].toUpperCase() + str.substring(1) : str;
|
|
242
|
+
}
|
|
243
|
+
function getDataServiceKeys(options) {
|
|
244
|
+
const filterKey = options.collection ? `${options.collection}Filter` : 'filter';
|
|
245
|
+
const selectedIdsKey = options.collection ? `selected${capitalize$1(options.collection)}Ids` : 'selectedIds';
|
|
246
|
+
const selectedEntitiesKey = options.collection ? `selected${capitalize$1(options.collection)}Entities` : 'selectedEntities';
|
|
247
|
+
const updateFilterKey = options.collection ? `update${capitalize$1(options.collection)}Filter` : 'updateFilter';
|
|
248
|
+
const updateSelectedKey = options.collection ? `updateSelected${capitalize$1(options.collection)}Entities` : 'updateSelected';
|
|
249
|
+
const loadKey = options.collection ? `load${capitalize$1(options.collection)}Entities` : 'load';
|
|
250
|
+
const currentKey = options.collection ? `current${capitalize$1(options.collection)}` : 'current';
|
|
251
|
+
const loadByIdKey = options.collection ? `load${capitalize$1(options.collection)}ById` : 'loadById';
|
|
252
|
+
const setCurrentKey = options.collection ? `setCurrent${capitalize$1(options.collection)}` : 'setCurrent';
|
|
253
|
+
const createKey = options.collection ? `create${capitalize$1(options.collection)}` : 'create';
|
|
254
|
+
const updateKey = options.collection ? `update${capitalize$1(options.collection)}` : 'update';
|
|
255
|
+
const updateAllKey = options.collection ? `updateAll${capitalize$1(options.collection)}` : 'updateAll';
|
|
256
|
+
const deleteKey = options.collection ? `delete${capitalize$1(options.collection)}` : 'delete';
|
|
257
|
+
// TODO: Take these from @ngrx/signals/entities, when they are exported
|
|
258
|
+
const entitiesKey = options.collection ? `${options.collection}Entities` : 'entities';
|
|
259
|
+
const entityMapKey = options.collection ? `${options.collection}EntityMap` : 'entityMap';
|
|
260
|
+
const idsKey = options.collection ? `${options.collection}Ids` : 'ids';
|
|
261
|
+
return {
|
|
262
|
+
filterKey,
|
|
263
|
+
selectedIdsKey,
|
|
264
|
+
selectedEntitiesKey,
|
|
265
|
+
updateFilterKey,
|
|
266
|
+
updateSelectedKey,
|
|
267
|
+
loadKey,
|
|
268
|
+
entitiesKey,
|
|
269
|
+
entityMapKey,
|
|
270
|
+
idsKey,
|
|
271
|
+
currentKey,
|
|
272
|
+
loadByIdKey,
|
|
273
|
+
setCurrentKey,
|
|
274
|
+
createKey,
|
|
275
|
+
updateKey,
|
|
276
|
+
updateAllKey,
|
|
277
|
+
deleteKey
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
281
|
+
function withDataService(options) {
|
|
282
|
+
const { dataServiceType, filter, collection: prefix } = options;
|
|
283
|
+
const { entitiesKey, filterKey, loadKey, selectedEntitiesKey, selectedIdsKey, updateFilterKey, updateSelectedKey, currentKey, createKey, updateKey, updateAllKey, deleteKey, loadByIdKey, setCurrentKey } = getDataServiceKeys(options);
|
|
284
|
+
const { callStateKey } = getCallStateKeys({ collection: prefix });
|
|
285
|
+
return signalStoreFeature(withState(() => ({
|
|
286
|
+
[filterKey]: filter,
|
|
287
|
+
[selectedIdsKey]: {},
|
|
288
|
+
[currentKey]: undefined
|
|
289
|
+
})), withComputed((store) => {
|
|
290
|
+
const entities = store[entitiesKey];
|
|
291
|
+
const selectedIds = store[selectedIdsKey];
|
|
292
|
+
return {
|
|
293
|
+
[selectedEntitiesKey]: computed(() => entities().filter(e => selectedIds()[e.id]))
|
|
294
|
+
};
|
|
295
|
+
}), withMethods((store) => {
|
|
296
|
+
const dataService = inject(dataServiceType);
|
|
297
|
+
return {
|
|
298
|
+
[updateFilterKey]: (filter) => {
|
|
299
|
+
patchState$1(store, { [filterKey]: filter });
|
|
300
|
+
},
|
|
301
|
+
[updateSelectedKey]: (id, selected) => {
|
|
302
|
+
patchState$1(store, (state) => ({
|
|
303
|
+
[selectedIdsKey]: {
|
|
304
|
+
...state[selectedIdsKey],
|
|
305
|
+
[id]: selected,
|
|
306
|
+
}
|
|
307
|
+
}));
|
|
308
|
+
},
|
|
309
|
+
[loadKey]: async () => {
|
|
310
|
+
const filter = store[filterKey];
|
|
311
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
312
|
+
try {
|
|
313
|
+
const result = await dataService.load(filter());
|
|
314
|
+
patchState$1(store, prefix ? setAllEntities(result, { collection: prefix }) : setAllEntities(result));
|
|
315
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
316
|
+
}
|
|
317
|
+
catch (e) {
|
|
318
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
319
|
+
throw e;
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
[loadByIdKey]: async (id) => {
|
|
323
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
324
|
+
try {
|
|
325
|
+
const current = await dataService.loadById(id);
|
|
326
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
327
|
+
patchState$1(store, { [currentKey]: current });
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
331
|
+
throw e;
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
[setCurrentKey]: (current) => {
|
|
335
|
+
patchState$1(store, { [currentKey]: current });
|
|
336
|
+
},
|
|
337
|
+
[createKey]: async (entity) => {
|
|
338
|
+
patchState$1(store, { [currentKey]: entity });
|
|
339
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
340
|
+
try {
|
|
341
|
+
const created = await dataService.create(entity);
|
|
342
|
+
patchState$1(store, { [currentKey]: created });
|
|
343
|
+
patchState$1(store, prefix ? addEntity(created, { collection: prefix }) : addEntity(created));
|
|
344
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
348
|
+
throw e;
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
[updateKey]: async (entity) => {
|
|
352
|
+
patchState$1(store, { [currentKey]: entity });
|
|
353
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
354
|
+
try {
|
|
355
|
+
const updated = await dataService.update(entity);
|
|
356
|
+
patchState$1(store, { [currentKey]: updated });
|
|
357
|
+
// Why do we need this cast to Partial<Entity>?
|
|
358
|
+
const updateArg = { id: updated.id, changes: updated };
|
|
359
|
+
patchState$1(store, prefix ? updateEntity(updateArg, { collection: prefix }) : updateEntity(updateArg));
|
|
360
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
364
|
+
throw e;
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
[updateAllKey]: async (entities) => {
|
|
368
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
369
|
+
try {
|
|
370
|
+
const result = await dataService.updateAll(entities);
|
|
371
|
+
patchState$1(store, prefix ? setAllEntities(result, { collection: prefix }) : setAllEntities(result));
|
|
372
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
376
|
+
throw e;
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
[deleteKey]: async (entity) => {
|
|
380
|
+
patchState$1(store, { [currentKey]: entity });
|
|
381
|
+
store[callStateKey] && patchState$1(store, setLoading(prefix));
|
|
382
|
+
try {
|
|
383
|
+
await dataService.delete(entity);
|
|
384
|
+
patchState$1(store, { [currentKey]: undefined });
|
|
385
|
+
patchState$1(store, prefix ? removeEntity(entity.id, { collection: prefix }) : removeEntity(entity.id));
|
|
386
|
+
store[callStateKey] && patchState$1(store, setLoaded(prefix));
|
|
387
|
+
}
|
|
388
|
+
catch (e) {
|
|
389
|
+
store[callStateKey] && patchState$1(store, setError(e, prefix));
|
|
390
|
+
throw e;
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
}));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const defaultOptions = {
|
|
398
|
+
maxStackSize: 100
|
|
399
|
+
};
|
|
400
|
+
function getUndoRedoKeys(collections) {
|
|
401
|
+
if (collections) {
|
|
402
|
+
return collections.flatMap(c => [`${c}EntityMap`, `${c}Ids`, `selected${capitalize$1(c)}Ids`, `${c}Filter`]);
|
|
403
|
+
}
|
|
404
|
+
return ['entityMap', 'ids', 'selectedIds', 'filter'];
|
|
405
|
+
}
|
|
406
|
+
function withUndoRedo(options = {}) {
|
|
407
|
+
let previous = null;
|
|
408
|
+
let skipOnce = false;
|
|
409
|
+
const normalized = {
|
|
410
|
+
...defaultOptions,
|
|
411
|
+
...options
|
|
412
|
+
};
|
|
413
|
+
//
|
|
414
|
+
// Design Decision: This feature has its own
|
|
415
|
+
// internal state.
|
|
416
|
+
//
|
|
417
|
+
const undoStack = [];
|
|
418
|
+
const redoStack = [];
|
|
419
|
+
const canUndo = signal(false);
|
|
420
|
+
const canRedo = signal(false);
|
|
421
|
+
const updateInternal = () => {
|
|
422
|
+
canUndo.set(undoStack.length !== 0);
|
|
423
|
+
canRedo.set(redoStack.length !== 0);
|
|
424
|
+
};
|
|
425
|
+
const keys = getUndoRedoKeys(normalized?.collections);
|
|
426
|
+
return signalStoreFeature(withComputed(() => ({
|
|
427
|
+
canUndo: canUndo.asReadonly(),
|
|
428
|
+
canRedo: canRedo.asReadonly()
|
|
429
|
+
})), withMethods((store) => ({
|
|
430
|
+
undo() {
|
|
431
|
+
const item = undoStack.pop();
|
|
432
|
+
if (item && previous) {
|
|
433
|
+
redoStack.push(previous);
|
|
434
|
+
}
|
|
435
|
+
if (item) {
|
|
436
|
+
skipOnce = true;
|
|
437
|
+
patchState$1(store, item);
|
|
438
|
+
previous = item;
|
|
439
|
+
}
|
|
440
|
+
updateInternal();
|
|
441
|
+
},
|
|
442
|
+
redo() {
|
|
443
|
+
const item = redoStack.pop();
|
|
444
|
+
if (item && previous) {
|
|
445
|
+
undoStack.push(previous);
|
|
446
|
+
}
|
|
447
|
+
if (item) {
|
|
448
|
+
skipOnce = true;
|
|
449
|
+
patchState$1(store, item);
|
|
450
|
+
previous = item;
|
|
451
|
+
}
|
|
452
|
+
updateInternal();
|
|
453
|
+
}
|
|
454
|
+
})), withHooks({
|
|
455
|
+
onInit(store) {
|
|
456
|
+
effect(() => {
|
|
457
|
+
const cand = keys.reduce((acc, key) => {
|
|
458
|
+
const s = store[key];
|
|
459
|
+
if (s && isSignal(s)) {
|
|
460
|
+
return {
|
|
461
|
+
...acc,
|
|
462
|
+
[key]: s()
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
return acc;
|
|
466
|
+
}, {});
|
|
467
|
+
if (skipOnce) {
|
|
468
|
+
skipOnce = false;
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
// Clear redoStack after recorded action
|
|
472
|
+
redoStack.splice(0);
|
|
473
|
+
if (previous) {
|
|
474
|
+
undoStack.push(previous);
|
|
475
|
+
}
|
|
476
|
+
if (redoStack.length > normalized.maxStackSize) {
|
|
477
|
+
undoStack.unshift();
|
|
478
|
+
}
|
|
479
|
+
previous = cand;
|
|
480
|
+
// Don't propogate current reactive context
|
|
481
|
+
untracked(() => updateInternal());
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
}));
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
const NOOP = () => { };
|
|
488
|
+
const StorageSyncStub = {
|
|
489
|
+
clearStorage: NOOP,
|
|
490
|
+
readFromStorage: NOOP,
|
|
491
|
+
writeToStorage: NOOP,
|
|
492
|
+
};
|
|
493
|
+
function withStorageSync(configOrKey) {
|
|
494
|
+
const { key, autoSync = true, select = (state) => state, parse = JSON.parse, stringify = JSON.stringify, storage: storageFactory = () => localStorage, } = typeof configOrKey === 'string' ? { key: configOrKey } : configOrKey;
|
|
495
|
+
return signalStoreFeature(withMethods((store, platformId = inject(PLATFORM_ID)) => {
|
|
496
|
+
if (isPlatformServer(platformId)) {
|
|
497
|
+
console.warn(`'withStorageSync' provides non-functional implementation due to server-side execution`);
|
|
498
|
+
return StorageSyncStub;
|
|
499
|
+
}
|
|
500
|
+
const storage = storageFactory();
|
|
501
|
+
return {
|
|
502
|
+
/**
|
|
503
|
+
* Removes the item stored in storage.
|
|
504
|
+
*/
|
|
505
|
+
clearStorage() {
|
|
506
|
+
storage.removeItem(key);
|
|
507
|
+
},
|
|
508
|
+
/**
|
|
509
|
+
* Reads item from storage and patches the state.
|
|
510
|
+
*/
|
|
511
|
+
readFromStorage() {
|
|
512
|
+
const stateString = storage.getItem(key);
|
|
513
|
+
if (stateString) {
|
|
514
|
+
patchState$1(store, parse(stateString));
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
/**
|
|
518
|
+
* Writes selected portion to storage.
|
|
519
|
+
*/
|
|
520
|
+
writeToStorage() {
|
|
521
|
+
const slicedState = select(getState(store));
|
|
522
|
+
storage.setItem(key, stringify(slicedState));
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
}), withHooks({
|
|
526
|
+
onInit(store, platformId = inject(PLATFORM_ID)) {
|
|
527
|
+
if (isPlatformServer(platformId)) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
if (autoSync) {
|
|
531
|
+
store.readFromStorage();
|
|
532
|
+
effect(() => {
|
|
533
|
+
store.writeToStorage();
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
},
|
|
537
|
+
}));
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function isUnsubscribable(fn) {
|
|
541
|
+
return !!fn?.unsubscribe;
|
|
542
|
+
}
|
|
543
|
+
function capitalize(str) {
|
|
544
|
+
return str ? str[0].toUpperCase() + str.substring(1) : str;
|
|
545
|
+
}
|
|
546
|
+
function isActionCreator(action) {
|
|
547
|
+
return (typeof action === 'function' &&
|
|
548
|
+
action &&
|
|
549
|
+
action.type &&
|
|
550
|
+
typeof action.type === 'string');
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
class SignalReduxStore {
|
|
554
|
+
constructor() {
|
|
555
|
+
this.mapperDict = {};
|
|
556
|
+
this.dispatch = rxMethod(pipe(tap((action) => {
|
|
557
|
+
const callbacks = this.mapperDict[action.type];
|
|
558
|
+
if (callbacks?.storeMethod) {
|
|
559
|
+
if (isUnsubscribable(callbacks.storeMethod) &&
|
|
560
|
+
callbacks.resultMethod) {
|
|
561
|
+
return callbacks.storeMethod(action, (a) => {
|
|
562
|
+
const resultAction = callbacks.resultMethod?.(a);
|
|
563
|
+
this.dispatch(resultAction);
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
return callbacks?.storeMethod(action);
|
|
567
|
+
}
|
|
568
|
+
return;
|
|
569
|
+
})));
|
|
570
|
+
}
|
|
571
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
572
|
+
connectFeatureStore(mappers) {
|
|
573
|
+
mappers.forEach(mapper => mapper.types.forEach(action => this.mapperDict[action] = {
|
|
574
|
+
storeMethod: mapper.storeMethod,
|
|
575
|
+
resultMethod: mapper.resultMethod
|
|
576
|
+
}));
|
|
577
|
+
}
|
|
578
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: SignalReduxStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
579
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: SignalReduxStore, providedIn: 'root' }); }
|
|
580
|
+
}
|
|
581
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.0.9", ngImport: i0, type: SignalReduxStore, decorators: [{
|
|
582
|
+
type: Injectable,
|
|
583
|
+
args: [{
|
|
584
|
+
providedIn: 'root'
|
|
585
|
+
}]
|
|
586
|
+
}] });
|
|
587
|
+
function injectReduxDispatch() {
|
|
588
|
+
return inject(SignalReduxStore).dispatch;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function mapAction(...args) {
|
|
592
|
+
let resultMethod = args.pop();
|
|
593
|
+
let storeMethod = args.pop();
|
|
594
|
+
if (isActionCreator(storeMethod)) {
|
|
595
|
+
args.push(storeMethod);
|
|
596
|
+
storeMethod = resultMethod || storeMethod;
|
|
597
|
+
resultMethod = undefined;
|
|
598
|
+
}
|
|
599
|
+
const types = args.map((creator) => creator.type);
|
|
600
|
+
return {
|
|
601
|
+
types,
|
|
602
|
+
storeMethod,
|
|
603
|
+
resultMethod
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function withActionMappers(...mappers) {
|
|
607
|
+
return mappers;
|
|
608
|
+
}
|
|
609
|
+
function createReduxState(storeName, signalStore, withActionMappers) {
|
|
610
|
+
const isRootProvider = signalStore?.ɵprov?.providedIn === 'root';
|
|
611
|
+
return {
|
|
612
|
+
[`provide${capitalize(storeName)}Store`]: (connectReduxDevtools = false) => makeEnvironmentProviders([
|
|
613
|
+
isRootProvider ? [] : signalStore,
|
|
614
|
+
{
|
|
615
|
+
provide: ENVIRONMENT_INITIALIZER,
|
|
616
|
+
multi: true,
|
|
617
|
+
useFactory: (signalReduxStore = inject(SignalReduxStore), store = inject(signalStore)) => () => {
|
|
618
|
+
if (connectReduxDevtools) {
|
|
619
|
+
// addStoreToReduxDevtools(store, storeName, false);
|
|
620
|
+
}
|
|
621
|
+
signalReduxStore.connectFeatureStore(withActionMappers(store));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
]),
|
|
625
|
+
[`inject${capitalize(storeName)}Store`]: () => Object.assign(inject(signalStore), { dispatch: injectReduxDispatch() })
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
function reduxMethod(generator, resultMethodOrConfig, config) {
|
|
630
|
+
const injector = inject(Injector);
|
|
631
|
+
if (typeof resultMethodOrConfig === 'function') {
|
|
632
|
+
let unsubscribable;
|
|
633
|
+
const inputResultFn = ((input, resultMethod = resultMethodOrConfig) => {
|
|
634
|
+
const rxMethodWithResult = rxMethod(pipe(generator, map(resultMethod)), {
|
|
635
|
+
...(config || {}),
|
|
636
|
+
injector: config?.injector || injector
|
|
637
|
+
});
|
|
638
|
+
const rxWithInput = rxMethodWithResult(input);
|
|
639
|
+
unsubscribable = { unsubscribe: rxWithInput.unsubscribe.bind(rxWithInput) };
|
|
640
|
+
return rxWithInput;
|
|
641
|
+
});
|
|
642
|
+
inputResultFn.unsubscribe = () => unsubscribable?.unsubscribe();
|
|
643
|
+
return inputResultFn;
|
|
644
|
+
}
|
|
645
|
+
return rxMethod(generator, resultMethodOrConfig);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Generated bundle index. Do not edit.
|
|
650
|
+
*/
|
|
651
|
+
|
|
652
|
+
export { capitalize$1 as capitalize, createReduxState, getCallStateKeys, getDataServiceKeys, getUndoRedoKeys, mapAction, noPayload, patchState, payload, reduxMethod, setError, setLoaded, setLoading, withActionMappers, withCallState, withDataService, withDevtools, withRedux, withStorageSync, withUndoRedo };
|
|
653
|
+
//# sourceMappingURL=angular-architects-ngrx-toolkit.mjs.map
|