@angular-architects/ngrx-toolkit 20.0.2 → 20.1.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.
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs +119 -0
- package/fesm2022/angular-architects-ngrx-toolkit-redux-connector.mjs.map +1 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs +1888 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
- package/index.d.ts +1084 -0
- package/package.json +21 -4
- package/redux-connector/index.d.ts +59 -0
- package/eslint.config.cjs +0 -43
- package/jest.config.ts +0 -22
- package/ng-package.json +0 -7
- package/project.json +0 -37
- package/redux-connector/docs/README.md +0 -131
- package/redux-connector/index.ts +0 -6
- package/redux-connector/ng-package.json +0 -5
- package/redux-connector/src/lib/create-redux.ts +0 -102
- package/redux-connector/src/lib/model.ts +0 -89
- package/redux-connector/src/lib/rxjs-interop/redux-method.ts +0 -66
- package/redux-connector/src/lib/signal-redux-store.ts +0 -59
- package/redux-connector/src/lib/util.ts +0 -22
- package/src/index.ts +0 -43
- package/src/lib/assertions/assertions.ts +0 -9
- package/src/lib/devtools/features/with-disabled-name-indicies.ts +0 -31
- package/src/lib/devtools/features/with-glitch-tracking.ts +0 -35
- package/src/lib/devtools/features/with-mapper.ts +0 -34
- package/src/lib/devtools/internal/current-action-names.ts +0 -1
- package/src/lib/devtools/internal/default-tracker.ts +0 -60
- package/src/lib/devtools/internal/devtools-feature.ts +0 -37
- package/src/lib/devtools/internal/devtools-syncer.service.ts +0 -202
- package/src/lib/devtools/internal/glitch-tracker.service.ts +0 -61
- package/src/lib/devtools/internal/models.ts +0 -29
- package/src/lib/devtools/provide-devtools-config.ts +0 -32
- package/src/lib/devtools/rename-devtools-name.ts +0 -21
- package/src/lib/devtools/tests/action-name.spec.ts +0 -48
- package/src/lib/devtools/tests/basic.spec.ts +0 -111
- package/src/lib/devtools/tests/connecting.spec.ts +0 -37
- package/src/lib/devtools/tests/helpers.spec.ts +0 -43
- package/src/lib/devtools/tests/naming.spec.ts +0 -216
- package/src/lib/devtools/tests/provide-devtools-config.spec.ts +0 -25
- package/src/lib/devtools/tests/types.spec.ts +0 -19
- package/src/lib/devtools/tests/update-state.spec.ts +0 -29
- package/src/lib/devtools/tests/with-devtools.spec.ts +0 -5
- package/src/lib/devtools/tests/with-glitch-tracking.spec.ts +0 -272
- package/src/lib/devtools/tests/with-mapper.spec.ts +0 -69
- package/src/lib/devtools/update-state.ts +0 -38
- package/src/lib/devtools/with-dev-tools-stub.ts +0 -6
- package/src/lib/devtools/with-devtools.ts +0 -81
- package/src/lib/immutable-state/deep-freeze.ts +0 -43
- package/src/lib/immutable-state/is-dev-mode.ts +0 -6
- package/src/lib/immutable-state/tests/with-immutable-state.spec.ts +0 -278
- package/src/lib/immutable-state/with-immutable-state.ts +0 -150
- package/src/lib/shared/prettify.ts +0 -3
- package/src/lib/shared/signal-store-models.ts +0 -30
- package/src/lib/shared/throw-if-null.ts +0 -7
- package/src/lib/storage-sync/features/with-indexed-db.ts +0 -81
- package/src/lib/storage-sync/features/with-local-storage.ts +0 -58
- package/src/lib/storage-sync/internal/indexeddb.service.ts +0 -124
- package/src/lib/storage-sync/internal/local-storage.service.ts +0 -19
- package/src/lib/storage-sync/internal/models.ts +0 -62
- package/src/lib/storage-sync/internal/session-storage.service.ts +0 -18
- package/src/lib/storage-sync/tests/indexeddb.service.spec.ts +0 -99
- package/src/lib/storage-sync/tests/with-storage-async.spec.ts +0 -305
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -273
- package/src/lib/storage-sync/with-storage-sync.ts +0 -236
- package/src/lib/with-call-state.spec.ts +0 -42
- package/src/lib/with-call-state.ts +0 -195
- package/src/lib/with-conditional.spec.ts +0 -125
- package/src/lib/with-conditional.ts +0 -74
- package/src/lib/with-data-service.spec.ts +0 -564
- package/src/lib/with-data-service.ts +0 -433
- package/src/lib/with-feature-factory.spec.ts +0 -69
- package/src/lib/with-feature-factory.ts +0 -56
- package/src/lib/with-pagination.spec.ts +0 -135
- package/src/lib/with-pagination.ts +0 -373
- package/src/lib/with-redux.spec.ts +0 -258
- package/src/lib/with-redux.ts +0 -387
- package/src/lib/with-reset.spec.ts +0 -112
- package/src/lib/with-reset.ts +0 -62
- package/src/lib/with-undo-redo.spec.ts +0 -274
- package/src/lib/with-undo-redo.ts +0 -200
- package/src/test-setup.ts +0 -6
- package/tsconfig.json +0 -29
- package/tsconfig.lib.json +0 -17
- package/tsconfig.lib.prod.json +0 -9
- package/tsconfig.spec.json +0 -17
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { getState, patchState, signalStore, withState } from '@ngrx/signals';
|
|
3
|
-
import * as devMode from '../is-dev-mode';
|
|
4
|
-
import { withImmutableState } from '../with-immutable-state';
|
|
5
|
-
|
|
6
|
-
describe('withImmutableState', () => {
|
|
7
|
-
const SECRET = Symbol('secret');
|
|
8
|
-
|
|
9
|
-
const createInitialState = () => ({
|
|
10
|
-
user: {
|
|
11
|
-
firstName: 'John',
|
|
12
|
-
lastName: 'Smith',
|
|
13
|
-
},
|
|
14
|
-
foo: 'bar',
|
|
15
|
-
numbers: [1, 2, 3],
|
|
16
|
-
ngrx: 'signals',
|
|
17
|
-
nestedSymbol: {
|
|
18
|
-
[SECRET]: 'another secret',
|
|
19
|
-
},
|
|
20
|
-
[SECRET]: {
|
|
21
|
-
code: 'secret',
|
|
22
|
-
value: '123',
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const createStore = (enableInProduction: boolean | undefined) => {
|
|
27
|
-
const initialState = createInitialState();
|
|
28
|
-
const Store = signalStore(
|
|
29
|
-
{ protectedState: false },
|
|
30
|
-
enableInProduction === undefined
|
|
31
|
-
? withImmutableState(initialState)
|
|
32
|
-
: withImmutableState(initialState, { enableInProduction }),
|
|
33
|
-
);
|
|
34
|
-
return TestBed.configureTestingModule({ providers: [Store] }).inject(Store);
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
for (const isDevMode of [true, false]) {
|
|
38
|
-
describe(isDevMode ? 'dev mode' : 'production mode', () => {
|
|
39
|
-
beforeEach(() => {
|
|
40
|
-
jest.spyOn(devMode, 'isDevMode').mockReturnValue(isDevMode);
|
|
41
|
-
});
|
|
42
|
-
for (const { stateFactory, enableInProduction, name, protectionOn } of [
|
|
43
|
-
{
|
|
44
|
-
name: 'dev-only protection',
|
|
45
|
-
stateFactory: () => createStore(false),
|
|
46
|
-
enableInProduction: false,
|
|
47
|
-
protectionOn: isDevMode,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'production protection',
|
|
51
|
-
enableInProduction: true,
|
|
52
|
-
stateFactory: () => createStore(true),
|
|
53
|
-
protectionOn: true,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
name: 'default settings',
|
|
57
|
-
enableInProduction: undefined,
|
|
58
|
-
stateFactory: () => createStore(undefined),
|
|
59
|
-
protectionOn: isDevMode,
|
|
60
|
-
},
|
|
61
|
-
]) {
|
|
62
|
-
describe(name, () => {
|
|
63
|
-
it(`throws not on a mutable change (primitive type)`, () => {
|
|
64
|
-
const state = stateFactory();
|
|
65
|
-
|
|
66
|
-
const patch = () =>
|
|
67
|
-
patchState(state, (state) => {
|
|
68
|
-
state.ngrx = 'mutable change';
|
|
69
|
-
return state;
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
expect(patch).not.toThrow();
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it(`throws ${protectionOn ? '' : 'not '}on a mutable change (object type)`, () => {
|
|
76
|
-
const state = stateFactory();
|
|
77
|
-
|
|
78
|
-
const patch = () =>
|
|
79
|
-
patchState(state, (state) => {
|
|
80
|
-
state.user.firstName = 'Siegfried';
|
|
81
|
-
return state;
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
if (protectionOn) {
|
|
85
|
-
expect(patch).toThrow(
|
|
86
|
-
"Cannot assign to read only property 'firstName' of object",
|
|
87
|
-
);
|
|
88
|
-
} else {
|
|
89
|
-
expect(patch).not.toThrow();
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it(`does ${
|
|
94
|
-
protectionOn ? '' : 'not '
|
|
95
|
-
}on a nested mutable change`, () => {
|
|
96
|
-
const state = stateFactory();
|
|
97
|
-
|
|
98
|
-
const patch = () =>
|
|
99
|
-
patchState(state, (state) => {
|
|
100
|
-
state.user.firstName = 'mutable change';
|
|
101
|
-
return state;
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
if (protectionOn) {
|
|
105
|
-
expect(patch).toThrow(
|
|
106
|
-
"Cannot assign to read only property 'firstName' of object",
|
|
107
|
-
);
|
|
108
|
-
} else {
|
|
109
|
-
expect(patch).not.toThrow();
|
|
110
|
-
}
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
describe('mutable changes outside of patchState', () => {
|
|
114
|
-
it(`throws${
|
|
115
|
-
protectionOn ? '' : ' not'
|
|
116
|
-
} on reassigned a property of the exposed state`, () => {
|
|
117
|
-
const state = stateFactory();
|
|
118
|
-
const patch = () => {
|
|
119
|
-
state.user().firstName = 'mutable change 1';
|
|
120
|
-
};
|
|
121
|
-
if (protectionOn) {
|
|
122
|
-
expect(patch).toThrow(
|
|
123
|
-
"Cannot assign to read only property 'firstName' of object",
|
|
124
|
-
);
|
|
125
|
-
} else {
|
|
126
|
-
expect(patch).not.toThrow();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it(`throws not when exposed state (primitive type) via getState is mutated`, () => {
|
|
131
|
-
const state = stateFactory();
|
|
132
|
-
const s = getState(state);
|
|
133
|
-
|
|
134
|
-
const patch = () => (s.ngrx = 'mutable change 2');
|
|
135
|
-
expect(patch).not.toThrow();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it(`throws ${protectionOn ? '' : 'not '} when exposed state (object type) via getState is mutated`, () => {
|
|
139
|
-
const state = stateFactory();
|
|
140
|
-
const s = getState(state);
|
|
141
|
-
|
|
142
|
-
const patch = () => (s.user.firstName = 'Hans');
|
|
143
|
-
|
|
144
|
-
if (protectionOn) {
|
|
145
|
-
expect(patch).toThrow(
|
|
146
|
-
"Cannot assign to read only property 'firstName' of object",
|
|
147
|
-
);
|
|
148
|
-
} else {
|
|
149
|
-
expect(patch).not.toThrow();
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it(`throws ${
|
|
154
|
-
protectionOn ? '' : 'not '
|
|
155
|
-
}when mutable change happens`, () => {
|
|
156
|
-
const state = stateFactory();
|
|
157
|
-
const s = { user: { firstName: 'M', lastName: 'S' } };
|
|
158
|
-
patchState(state, s);
|
|
159
|
-
const patch = () => {
|
|
160
|
-
s.user.firstName = 'mutable change 3';
|
|
161
|
-
};
|
|
162
|
-
if (protectionOn) {
|
|
163
|
-
expect(patch).toThrow(
|
|
164
|
-
"Cannot assign to read only property 'firstName' of object",
|
|
165
|
-
);
|
|
166
|
-
} else {
|
|
167
|
-
expect(patch).not.toThrow();
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it(`throws ${
|
|
172
|
-
protectionOn ? '' : 'not '
|
|
173
|
-
}when mutable change of root symbol property happens`, () => {
|
|
174
|
-
const state = stateFactory();
|
|
175
|
-
const s = getState(state);
|
|
176
|
-
|
|
177
|
-
const patch = () => {
|
|
178
|
-
s[SECRET].code = 'mutable change';
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
if (protectionOn) {
|
|
182
|
-
expect(patch).toThrow(
|
|
183
|
-
"Cannot assign to read only property 'code' of object",
|
|
184
|
-
);
|
|
185
|
-
} else {
|
|
186
|
-
expect(patch).not.toThrow();
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it(`throws ${
|
|
191
|
-
protectionOn ? '' : 'not '
|
|
192
|
-
}when mutable change of nested symbol property happens`, () => {
|
|
193
|
-
const state = stateFactory();
|
|
194
|
-
const s = getState(state);
|
|
195
|
-
|
|
196
|
-
const patch = () => {
|
|
197
|
-
s.nestedSymbol[SECRET] = 'mutable change';
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
if (protectionOn) {
|
|
201
|
-
expect(patch).toThrow(
|
|
202
|
-
"Cannot assign to read only property 'Symbol(secret)' of object",
|
|
203
|
-
);
|
|
204
|
-
} else {
|
|
205
|
-
expect(patch).not.toThrow();
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
describe('special tests', () => {
|
|
211
|
-
for (const { name, mutationFn } of [
|
|
212
|
-
{
|
|
213
|
-
name: 'location',
|
|
214
|
-
mutationFn: (state: { location: { city: string } }) =>
|
|
215
|
-
(state.location.city = 'Paris'),
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
name: 'user',
|
|
219
|
-
mutationFn: (state: { user: { firstName: string } }) =>
|
|
220
|
-
(state.user.firstName = 'Jane'),
|
|
221
|
-
},
|
|
222
|
-
]) {
|
|
223
|
-
it(`throws ${
|
|
224
|
-
protectionOn ? '' : 'not '
|
|
225
|
-
}on concatenated state (${name})`, () => {
|
|
226
|
-
const UserStore = signalStore(
|
|
227
|
-
{ providedIn: 'root' },
|
|
228
|
-
withImmutableState(createInitialState(), {
|
|
229
|
-
enableInProduction,
|
|
230
|
-
}),
|
|
231
|
-
withImmutableState(
|
|
232
|
-
{ location: { city: 'London' } },
|
|
233
|
-
{ enableInProduction },
|
|
234
|
-
),
|
|
235
|
-
);
|
|
236
|
-
const store = TestBed.inject(UserStore);
|
|
237
|
-
const state = getState(store);
|
|
238
|
-
|
|
239
|
-
if (protectionOn) {
|
|
240
|
-
expect(() => mutationFn(state)).toThrow();
|
|
241
|
-
} else {
|
|
242
|
-
expect(() => mutationFn(state)).not.toThrow();
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should be possible to mix both mutable and immutable state', () => {
|
|
249
|
-
const immutableState = {
|
|
250
|
-
user: {
|
|
251
|
-
id: 1,
|
|
252
|
-
name: 'John',
|
|
253
|
-
},
|
|
254
|
-
};
|
|
255
|
-
|
|
256
|
-
const mutableState = {
|
|
257
|
-
address: 'London',
|
|
258
|
-
};
|
|
259
|
-
const TestStore = signalStore(
|
|
260
|
-
{ providedIn: 'root', protectedState: false },
|
|
261
|
-
withImmutableState(immutableState, { enableInProduction }),
|
|
262
|
-
withState(mutableState),
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
TestBed.inject(TestStore);
|
|
266
|
-
|
|
267
|
-
if (protectionOn) {
|
|
268
|
-
expect(() => (immutableState.user.name = 'Jane')).toThrow();
|
|
269
|
-
} else {
|
|
270
|
-
expect(() => (immutableState.user.name = 'Jane')).not.toThrow();
|
|
271
|
-
}
|
|
272
|
-
expect(() => (mutableState.address = 'Glastonbury')).not.toThrow();
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
EmptyFeatureResult,
|
|
3
|
-
getState,
|
|
4
|
-
signalStoreFeature,
|
|
5
|
-
SignalStoreFeature,
|
|
6
|
-
SignalStoreFeatureResult,
|
|
7
|
-
watchState,
|
|
8
|
-
withHooks,
|
|
9
|
-
withState,
|
|
10
|
-
} from '@ngrx/signals';
|
|
11
|
-
import { deepFreeze } from './deep-freeze';
|
|
12
|
-
import { isDevMode } from './is-dev-mode';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Starting from v20, the root properties of the state object
|
|
16
|
-
* are all of type `WritableSignal`.
|
|
17
|
-
*
|
|
18
|
-
* That means, we cannot freeze root properties, which are of a
|
|
19
|
-
* primitive data type.
|
|
20
|
-
*
|
|
21
|
-
* This is a breaking change, because in v19 the state was a Signal.
|
|
22
|
-
* We had the possibility to freeze that Signal's
|
|
23
|
-
* object and could therefore provide immutability for
|
|
24
|
-
* root properties of primitive data types.
|
|
25
|
-
*
|
|
26
|
-
* For example:
|
|
27
|
-
*
|
|
28
|
-
* ```ts
|
|
29
|
-
* const state = {
|
|
30
|
-
* id: 1, // was frozen in v19, but not in >= v20
|
|
31
|
-
* address: {
|
|
32
|
-
* street: 'Main St',
|
|
33
|
-
* city: 'Springfield',
|
|
34
|
-
* }
|
|
35
|
-
* }
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Prevents mutation of the state.
|
|
41
|
-
*
|
|
42
|
-
* This is done by applying `Object.freeze` to each root
|
|
43
|
-
* property of the state. Any mutable change within
|
|
44
|
-
* or outside the `SignalStore` will throw an error.
|
|
45
|
-
*
|
|
46
|
-
* Root properties of the state having a primitive data type
|
|
47
|
-
* are not supported.
|
|
48
|
-
*
|
|
49
|
-
* * For example:
|
|
50
|
-
*
|
|
51
|
-
* ```ts
|
|
52
|
-
* const state = {
|
|
53
|
-
* // ⛔️ are not frozen -> mutable changes possible
|
|
54
|
-
* id: 1,
|
|
55
|
-
*
|
|
56
|
-
* // ✅ are frozen -> mutable changes throw
|
|
57
|
-
* address: {
|
|
58
|
-
* street: 'Main St',
|
|
59
|
-
* city: 'Springfield',
|
|
60
|
-
* }
|
|
61
|
-
* }
|
|
62
|
-
* ```
|
|
63
|
-
*
|
|
64
|
-
* @param state the state object
|
|
65
|
-
* @param options enable protection in production (default: false)
|
|
66
|
-
*/
|
|
67
|
-
export function withImmutableState<State extends object>(
|
|
68
|
-
state: State,
|
|
69
|
-
options?: { enableInProduction?: boolean },
|
|
70
|
-
): SignalStoreFeature<
|
|
71
|
-
SignalStoreFeatureResult,
|
|
72
|
-
EmptyFeatureResult & { state: State }
|
|
73
|
-
>;
|
|
74
|
-
/**
|
|
75
|
-
* Prevents mutation of the state.
|
|
76
|
-
*
|
|
77
|
-
* This is done by applying `Object.freeze` to each root
|
|
78
|
-
* property of the state. Any mutable change within
|
|
79
|
-
* or outside the `SignalStore` will throw an error.
|
|
80
|
-
*
|
|
81
|
-
* Root properties of the state having a primitive data type
|
|
82
|
-
* are not supported.
|
|
83
|
-
*
|
|
84
|
-
* * For example:
|
|
85
|
-
*
|
|
86
|
-
* ```ts
|
|
87
|
-
* const state = {
|
|
88
|
-
* // ⛔️ are not frozen -> mutable changes possible
|
|
89
|
-
* id: 1,
|
|
90
|
-
*
|
|
91
|
-
* // ✅ are frozen -> mutable changes throw
|
|
92
|
-
* address: {
|
|
93
|
-
* street: 'Main St',
|
|
94
|
-
* city: 'Springfield',
|
|
95
|
-
* }
|
|
96
|
-
* }
|
|
97
|
-
* ```
|
|
98
|
-
*
|
|
99
|
-
* @param stateFactory a function returning the state object
|
|
100
|
-
* @param options enable protection in production (default: false)
|
|
101
|
-
*/
|
|
102
|
-
export function withImmutableState<State extends object>(
|
|
103
|
-
stateFactory: () => State,
|
|
104
|
-
options?: { enableInProduction?: boolean },
|
|
105
|
-
): SignalStoreFeature<
|
|
106
|
-
SignalStoreFeatureResult,
|
|
107
|
-
EmptyFeatureResult & { state: State }
|
|
108
|
-
>;
|
|
109
|
-
export function withImmutableState<State extends object>(
|
|
110
|
-
stateOrFactory: State | (() => State),
|
|
111
|
-
options?: { enableInProduction?: boolean },
|
|
112
|
-
): SignalStoreFeature<
|
|
113
|
-
SignalStoreFeatureResult,
|
|
114
|
-
EmptyFeatureResult & { state: State }
|
|
115
|
-
> {
|
|
116
|
-
const immutableState =
|
|
117
|
-
typeof stateOrFactory === 'function' ? stateOrFactory() : stateOrFactory;
|
|
118
|
-
const stateKeys = Reflect.ownKeys(immutableState);
|
|
119
|
-
|
|
120
|
-
const applyFreezing = isDevMode() || options?.enableInProduction === true;
|
|
121
|
-
return signalStoreFeature(
|
|
122
|
-
withState(immutableState),
|
|
123
|
-
withHooks((store) => ({
|
|
124
|
-
onInit() {
|
|
125
|
-
if (!applyFreezing) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* `immutableState` will be initially frozen. That is because
|
|
130
|
-
* of potential mutations outside the SignalStore
|
|
131
|
-
*
|
|
132
|
-
* ```ts
|
|
133
|
-
* const initialState = {user: {id: 1}};
|
|
134
|
-
* signalStore(withImmutableState(initialState));
|
|
135
|
-
*
|
|
136
|
-
* initialState.user.id = 2; // must throw immutability
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
deepFreeze(
|
|
140
|
-
getState(store) as Record<string | symbol, unknown>,
|
|
141
|
-
stateKeys,
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
watchState(store, (state) => {
|
|
145
|
-
deepFreeze(state as Record<string | symbol, unknown>, stateKeys);
|
|
146
|
-
});
|
|
147
|
-
},
|
|
148
|
-
})),
|
|
149
|
-
);
|
|
150
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains copies of types of the Signal Store which are not public.
|
|
3
|
-
*
|
|
4
|
-
* Since certain features depend on them, if we don't want to break
|
|
5
|
-
* the encapsulation of @ngrx/signals, we decided to copy them.
|
|
6
|
-
*
|
|
7
|
-
* Since TypeScript is based on structural typing, we can get away with it.
|
|
8
|
-
*
|
|
9
|
-
* If @ngrx/signals changes its internal types, we catch them via integration
|
|
10
|
-
* tests.
|
|
11
|
-
*
|
|
12
|
-
* Because of the "tight coupling", the toolkit doesn't have version range
|
|
13
|
-
* to @ngrx/signal, but is very precise.
|
|
14
|
-
*/
|
|
15
|
-
import { Signal } from '@angular/core';
|
|
16
|
-
import { EntityId } from '@ngrx/signals/entities';
|
|
17
|
-
|
|
18
|
-
// withEntites models
|
|
19
|
-
export type EntityState<Entity> = {
|
|
20
|
-
entityMap: Record<EntityId, Entity>;
|
|
21
|
-
ids: EntityId[];
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export type EntityComputed<Entity> = {
|
|
25
|
-
entities: Signal<Entity[]>;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type NamedEntityComputed<Entity, Collection extends string> = {
|
|
29
|
-
[K in keyof EntityComputed<Entity> as `${Collection}${Capitalize<K>}`]: EntityComputed<Entity>[K];
|
|
30
|
-
};
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { inject } from '@angular/core';
|
|
2
|
-
import { getState, patchState } from '@ngrx/signals';
|
|
3
|
-
import { IndexedDBService } from '../internal/indexeddb.service';
|
|
4
|
-
import {
|
|
5
|
-
AsyncMethods,
|
|
6
|
-
AsyncStorageStrategy,
|
|
7
|
-
AsyncStoreForFactory,
|
|
8
|
-
SYNC_STATUS,
|
|
9
|
-
} from '../internal/models';
|
|
10
|
-
import { SyncConfig } from '../with-storage-sync';
|
|
11
|
-
|
|
12
|
-
export function withIndexedDB<
|
|
13
|
-
State extends object,
|
|
14
|
-
>(): AsyncStorageStrategy<State> {
|
|
15
|
-
function factory(
|
|
16
|
-
{ key, parse, select, stringify }: Required<SyncConfig<State>>,
|
|
17
|
-
store: AsyncStoreForFactory<State>,
|
|
18
|
-
useStubs: boolean,
|
|
19
|
-
): AsyncMethods {
|
|
20
|
-
if (useStubs) {
|
|
21
|
-
return {
|
|
22
|
-
clearStorage: () => Promise.resolve(),
|
|
23
|
-
readFromStorage: () => Promise.resolve(),
|
|
24
|
-
writeToStorage: () => Promise.resolve(),
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const indexeddbService = inject(IndexedDBService);
|
|
29
|
-
|
|
30
|
-
function warnOnSyncing(mode: 'read' | 'write'): void {
|
|
31
|
-
if (store[SYNC_STATUS]() === 'syncing') {
|
|
32
|
-
const prettyMode = mode === 'read' ? 'Reading' : 'Writing';
|
|
33
|
-
console.warn(
|
|
34
|
-
`${prettyMode} to Store (${key}) happened during an ongoing synchronization process.`,
|
|
35
|
-
'Please ensure that the store is not in syncing state via `store.whenSynced()`.',
|
|
36
|
-
'Alternatively, you can disable the autoSync by passing `autoSync: false` in the config.',
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return {
|
|
42
|
-
/**
|
|
43
|
-
* Removes the item stored in storage.
|
|
44
|
-
*/
|
|
45
|
-
async clearStorage(): Promise<void> {
|
|
46
|
-
warnOnSyncing('write');
|
|
47
|
-
store[SYNC_STATUS].set('syncing');
|
|
48
|
-
patchState(store, {});
|
|
49
|
-
await indexeddbService.clear(key);
|
|
50
|
-
store[SYNC_STATUS].set('synced');
|
|
51
|
-
},
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Reads item from storage and patches the state.
|
|
55
|
-
*/
|
|
56
|
-
async readFromStorage(): Promise<void> {
|
|
57
|
-
warnOnSyncing('read');
|
|
58
|
-
store[SYNC_STATUS].set('syncing');
|
|
59
|
-
const stateString = await indexeddbService.getItem(key);
|
|
60
|
-
if (stateString) {
|
|
61
|
-
patchState(store, parse(stateString));
|
|
62
|
-
}
|
|
63
|
-
store[SYNC_STATUS].set('synced');
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Writes selected portion to storage.
|
|
68
|
-
*/
|
|
69
|
-
async writeToStorage(): Promise<void> {
|
|
70
|
-
warnOnSyncing('write');
|
|
71
|
-
store[SYNC_STATUS].set('syncing');
|
|
72
|
-
const slicedState = select(getState(store)) as State;
|
|
73
|
-
await indexeddbService.setItem(key, stringify(slicedState));
|
|
74
|
-
store[SYNC_STATUS].set('synced');
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
factory.type = 'async' as const;
|
|
79
|
-
|
|
80
|
-
return factory;
|
|
81
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { inject, Type } from '@angular/core';
|
|
2
|
-
import { getState, patchState } from '@ngrx/signals';
|
|
3
|
-
import { LocalStorageService } from '../internal/local-storage.service';
|
|
4
|
-
import { SyncStorageStrategy, SyncStoreForFactory } from '../internal/models';
|
|
5
|
-
import { SessionStorageService } from '../internal/session-storage.service';
|
|
6
|
-
import { SyncConfig } from '../with-storage-sync';
|
|
7
|
-
|
|
8
|
-
export function withLocalStorage<
|
|
9
|
-
State extends object,
|
|
10
|
-
>(): SyncStorageStrategy<State> {
|
|
11
|
-
return createSyncMethods<State>(LocalStorageService);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function withSessionStorage<State extends object>() {
|
|
15
|
-
return createSyncMethods<State>(SessionStorageService);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function createSyncMethods<State extends object>(
|
|
19
|
-
Storage: Type<LocalStorageService | SessionStorageService>,
|
|
20
|
-
): SyncStorageStrategy<State> {
|
|
21
|
-
function factory(
|
|
22
|
-
{ key, parse, select, stringify }: Required<SyncConfig<State>>,
|
|
23
|
-
store: SyncStoreForFactory<State>,
|
|
24
|
-
useStubs: boolean,
|
|
25
|
-
) {
|
|
26
|
-
if (useStubs) {
|
|
27
|
-
return {
|
|
28
|
-
clearStorage: () => undefined,
|
|
29
|
-
readFromStorage: () => undefined,
|
|
30
|
-
writeToStorage: () => undefined,
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const storage = inject(Storage);
|
|
35
|
-
|
|
36
|
-
return {
|
|
37
|
-
clearStorage(): void {
|
|
38
|
-
storage.clear(key);
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
readFromStorage(): void {
|
|
42
|
-
const stateString = storage.getItem(key);
|
|
43
|
-
|
|
44
|
-
if (stateString) {
|
|
45
|
-
patchState(store, parse(stateString));
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
writeToStorage() {
|
|
50
|
-
const slicedState = select(getState(store)) as State;
|
|
51
|
-
storage.setItem(key, stringify(slicedState));
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
factory.type = 'sync' as const;
|
|
56
|
-
|
|
57
|
-
return factory;
|
|
58
|
-
}
|