@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,273 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { getState, patchState, signalStore, withState } from '@ngrx/signals';
|
|
3
|
-
import { withLocalStorage } from '../features/with-local-storage';
|
|
4
|
-
import { withStorageSync } from '../with-storage-sync';
|
|
5
|
-
|
|
6
|
-
interface StateObject {
|
|
7
|
-
foo: string;
|
|
8
|
-
age: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const initialState: StateObject = {
|
|
12
|
-
foo: 'bar',
|
|
13
|
-
age: 18,
|
|
14
|
-
};
|
|
15
|
-
const key = 'FooBar';
|
|
16
|
-
|
|
17
|
-
describe('withStorageSync (sync storage)', () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
// make sure to start with a clean storage
|
|
20
|
-
localStorage.removeItem(key);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('adds methods for storage access to the store', () => {
|
|
24
|
-
TestBed.runInInjectionContext(() => {
|
|
25
|
-
const Store = signalStore(withStorageSync({ key }));
|
|
26
|
-
const store = new Store();
|
|
27
|
-
|
|
28
|
-
expect(Object.keys(store)).toEqual([
|
|
29
|
-
'clearStorage',
|
|
30
|
-
'readFromStorage',
|
|
31
|
-
'writeToStorage',
|
|
32
|
-
]);
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('offers manual sync using provided methods', () => {
|
|
37
|
-
TestBed.runInInjectionContext(() => {
|
|
38
|
-
// prefill storage
|
|
39
|
-
localStorage.setItem(
|
|
40
|
-
key,
|
|
41
|
-
JSON.stringify({
|
|
42
|
-
foo: 'baz',
|
|
43
|
-
age: 99,
|
|
44
|
-
} as StateObject),
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
const Store = signalStore(
|
|
48
|
-
{ protectedState: false },
|
|
49
|
-
withState(initialState),
|
|
50
|
-
withStorageSync({ key, autoSync: false }),
|
|
51
|
-
);
|
|
52
|
-
const store = new Store();
|
|
53
|
-
expect(getState(store)).toEqual(initialState);
|
|
54
|
-
|
|
55
|
-
store.readFromStorage();
|
|
56
|
-
expect(getState(store)).toEqual({
|
|
57
|
-
foo: 'baz',
|
|
58
|
-
age: 99,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
patchState(store, { ...initialState });
|
|
62
|
-
TestBed.flushEffects();
|
|
63
|
-
|
|
64
|
-
let storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
65
|
-
expect(storeItem).toEqual({
|
|
66
|
-
foo: 'baz',
|
|
67
|
-
age: 99,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
store.writeToStorage();
|
|
71
|
-
storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
72
|
-
expect(storeItem).toEqual({
|
|
73
|
-
...initialState,
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
store.clearStorage();
|
|
77
|
-
storeItem = localStorage.getItem(key);
|
|
78
|
-
expect(storeItem).toEqual(null);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('autoSync', () => {
|
|
83
|
-
it('inits from storage and write to storage on changes when set to `true`', () => {
|
|
84
|
-
TestBed.runInInjectionContext(() => {
|
|
85
|
-
// prefill storage
|
|
86
|
-
localStorage.setItem(
|
|
87
|
-
key,
|
|
88
|
-
JSON.stringify({
|
|
89
|
-
foo: 'baz',
|
|
90
|
-
age: 99,
|
|
91
|
-
} as StateObject),
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const Store = signalStore(
|
|
95
|
-
{ protectedState: false },
|
|
96
|
-
withState(initialState),
|
|
97
|
-
withStorageSync(key),
|
|
98
|
-
);
|
|
99
|
-
const store = new Store();
|
|
100
|
-
expect(getState(store)).toEqual({
|
|
101
|
-
foo: 'baz',
|
|
102
|
-
age: 99,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
patchState(store, { ...initialState });
|
|
106
|
-
TestBed.flushEffects();
|
|
107
|
-
|
|
108
|
-
expect(getState(store)).toEqual({
|
|
109
|
-
...initialState,
|
|
110
|
-
});
|
|
111
|
-
const storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
112
|
-
expect(storeItem).toEqual({
|
|
113
|
-
...initialState,
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('does not init from storage and does write to storage on changes when set to `false`', () => {
|
|
119
|
-
TestBed.runInInjectionContext(() => {
|
|
120
|
-
// prefill storage
|
|
121
|
-
localStorage.setItem(
|
|
122
|
-
key,
|
|
123
|
-
JSON.stringify({
|
|
124
|
-
foo: 'baz',
|
|
125
|
-
age: 99,
|
|
126
|
-
} as StateObject),
|
|
127
|
-
);
|
|
128
|
-
|
|
129
|
-
const Store = signalStore(
|
|
130
|
-
{ protectedState: false },
|
|
131
|
-
withState(initialState),
|
|
132
|
-
withStorageSync({ key, autoSync: false }),
|
|
133
|
-
);
|
|
134
|
-
const store = new Store();
|
|
135
|
-
expect(getState(store)).toEqual(initialState);
|
|
136
|
-
|
|
137
|
-
patchState(store, { ...initialState });
|
|
138
|
-
const storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
139
|
-
expect(storeItem).toEqual({
|
|
140
|
-
foo: 'baz',
|
|
141
|
-
age: 99,
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
describe('select', () => {
|
|
148
|
-
it('syncs the whole state by default', () => {
|
|
149
|
-
TestBed.runInInjectionContext(() => {
|
|
150
|
-
const Store = signalStore(
|
|
151
|
-
{ protectedState: false },
|
|
152
|
-
withState(initialState),
|
|
153
|
-
withStorageSync(key),
|
|
154
|
-
);
|
|
155
|
-
const store = new Store();
|
|
156
|
-
|
|
157
|
-
patchState(store, { foo: 'baz', age: 25 });
|
|
158
|
-
|
|
159
|
-
const storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
160
|
-
expect(storeItem).toEqual({
|
|
161
|
-
foo: 'baz',
|
|
162
|
-
age: 25,
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('syncs selected slices when specified', () => {
|
|
168
|
-
TestBed.runInInjectionContext(() => {
|
|
169
|
-
const Store = signalStore(
|
|
170
|
-
{ protectedState: false },
|
|
171
|
-
withState(initialState),
|
|
172
|
-
withStorageSync({ key, select: ({ foo }) => ({ foo }) }),
|
|
173
|
-
);
|
|
174
|
-
const store = new Store();
|
|
175
|
-
|
|
176
|
-
patchState(store, { foo: 'baz' });
|
|
177
|
-
TestBed.flushEffects();
|
|
178
|
-
|
|
179
|
-
const storeItem = JSON.parse(localStorage.getItem(key) || '{}');
|
|
180
|
-
expect(storeItem).toEqual({
|
|
181
|
-
foo: 'baz',
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
describe('parse/stringify', () => {
|
|
188
|
-
it('uses custom parsing/stringification when specified', () => {
|
|
189
|
-
const parse = (stateString: string) => {
|
|
190
|
-
const [foo, age] = stateString.split('_');
|
|
191
|
-
return {
|
|
192
|
-
foo,
|
|
193
|
-
age: +age,
|
|
194
|
-
};
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
TestBed.runInInjectionContext(() => {
|
|
198
|
-
const Store = signalStore(
|
|
199
|
-
{ protectedState: false },
|
|
200
|
-
withState(initialState),
|
|
201
|
-
withStorageSync({
|
|
202
|
-
key,
|
|
203
|
-
parse,
|
|
204
|
-
stringify: (state) => `${state.foo}_${state.age}`,
|
|
205
|
-
}),
|
|
206
|
-
);
|
|
207
|
-
const store = new Store();
|
|
208
|
-
|
|
209
|
-
patchState(store, { foo: 'baz' });
|
|
210
|
-
TestBed.flushEffects();
|
|
211
|
-
|
|
212
|
-
const storeItem = parse(localStorage.getItem(key) || '');
|
|
213
|
-
expect(storeItem).toEqual({
|
|
214
|
-
...initialState,
|
|
215
|
-
foo: 'baz',
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
describe('storage factory', () => {
|
|
222
|
-
it('should throw an error when both config and storage strategy are provided', () => {
|
|
223
|
-
const signalStoreFactory = () =>
|
|
224
|
-
signalStore(
|
|
225
|
-
withStorageSync(
|
|
226
|
-
{ key: 'foo', storage: () => localStorage },
|
|
227
|
-
withLocalStorage(),
|
|
228
|
-
),
|
|
229
|
-
);
|
|
230
|
-
|
|
231
|
-
expect(signalStoreFactory).toThrow(
|
|
232
|
-
'You can either pass a storage strategy or a config with storage, but not both.',
|
|
233
|
-
);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
it('uses specified storage', () => {
|
|
237
|
-
TestBed.runInInjectionContext(() => {
|
|
238
|
-
// prefill storage
|
|
239
|
-
sessionStorage.setItem(
|
|
240
|
-
key,
|
|
241
|
-
JSON.stringify({
|
|
242
|
-
foo: 'baz',
|
|
243
|
-
age: 99,
|
|
244
|
-
} as StateObject),
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
const Store = signalStore(
|
|
248
|
-
{ protectedState: false },
|
|
249
|
-
withState(initialState),
|
|
250
|
-
withStorageSync({ key, storage: () => sessionStorage }),
|
|
251
|
-
);
|
|
252
|
-
const store = new Store();
|
|
253
|
-
expect(getState(store)).toEqual({
|
|
254
|
-
foo: 'baz',
|
|
255
|
-
age: 99,
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
patchState(store, { ...initialState });
|
|
259
|
-
TestBed.flushEffects();
|
|
260
|
-
|
|
261
|
-
expect(getState(store)).toEqual({
|
|
262
|
-
...initialState,
|
|
263
|
-
});
|
|
264
|
-
const storeItem = JSON.parse(sessionStorage.getItem(key) || '{}');
|
|
265
|
-
expect(storeItem).toEqual({
|
|
266
|
-
...initialState,
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
store.clearStorage();
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
});
|
|
273
|
-
});
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
import { isPlatformServer } from '@angular/common';
|
|
2
|
-
import { computed, effect, inject, PLATFORM_ID, signal } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
EmptyFeatureResult,
|
|
5
|
-
getState,
|
|
6
|
-
signalStoreFeature,
|
|
7
|
-
SignalStoreFeature,
|
|
8
|
-
SignalStoreFeatureResult,
|
|
9
|
-
watchState,
|
|
10
|
-
withHooks,
|
|
11
|
-
withMethods,
|
|
12
|
-
withProps,
|
|
13
|
-
} from '@ngrx/signals';
|
|
14
|
-
import {
|
|
15
|
-
withLocalStorage,
|
|
16
|
-
withSessionStorage,
|
|
17
|
-
} from './features/with-local-storage';
|
|
18
|
-
import {
|
|
19
|
-
AsyncFeatureResult,
|
|
20
|
-
AsyncStorageStrategy,
|
|
21
|
-
SYNC_STATUS,
|
|
22
|
-
SyncFeatureResult,
|
|
23
|
-
SyncStorageStrategy,
|
|
24
|
-
} from './internal/models';
|
|
25
|
-
|
|
26
|
-
export type SyncConfig<State> = {
|
|
27
|
-
/**
|
|
28
|
-
* The key which is used to access the storage.
|
|
29
|
-
*/
|
|
30
|
-
key: string;
|
|
31
|
-
/**
|
|
32
|
-
* Flag indicating if the store should read from storage on init and write to storage on every state change.
|
|
33
|
-
*
|
|
34
|
-
* `true` by default
|
|
35
|
-
*/
|
|
36
|
-
autoSync?: boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Function to select that portion of the state which should be stored.
|
|
39
|
-
*
|
|
40
|
-
* Returns the whole state object by default
|
|
41
|
-
*/
|
|
42
|
-
select?: (state: State) => unknown;
|
|
43
|
-
/**
|
|
44
|
-
* Function used to parse the state coming from storage.
|
|
45
|
-
*
|
|
46
|
-
* `JSON.parse()` by default
|
|
47
|
-
*/
|
|
48
|
-
parse?: (stateString: string) => State;
|
|
49
|
-
/**
|
|
50
|
-
* Function used to transform the state into a string representation.
|
|
51
|
-
*
|
|
52
|
-
* `JSON.stringify()` by default
|
|
53
|
-
*/
|
|
54
|
-
stringify?: (state: State) => string;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @deprecated Use {@link withSessionStorage} instead.
|
|
58
|
-
* Factory function used to switch to sessionStorage.
|
|
59
|
-
*
|
|
60
|
-
* `localStorage` by default
|
|
61
|
-
*/
|
|
62
|
-
storage?: () => Storage;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// only key
|
|
66
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
67
|
-
key: string,
|
|
68
|
-
): SignalStoreFeature<Input, SyncFeatureResult>;
|
|
69
|
-
|
|
70
|
-
// key + indexeddb
|
|
71
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
72
|
-
key: string,
|
|
73
|
-
storageStrategy: AsyncStorageStrategy<Input['state']>,
|
|
74
|
-
): SignalStoreFeature<Input, AsyncFeatureResult>;
|
|
75
|
-
|
|
76
|
-
// key + localStorage(or sessionStorage)
|
|
77
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
78
|
-
key: string,
|
|
79
|
-
storageStrategy: SyncStorageStrategy<Input['state']>,
|
|
80
|
-
): SignalStoreFeature<Input, SyncFeatureResult>;
|
|
81
|
-
|
|
82
|
-
// config + localStorage
|
|
83
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
84
|
-
config: SyncConfig<Input['state']>,
|
|
85
|
-
): SignalStoreFeature<Input, SyncFeatureResult>;
|
|
86
|
-
|
|
87
|
-
// config + indexeddb
|
|
88
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
89
|
-
config: SyncConfig<Input['state']>,
|
|
90
|
-
storageStrategy: AsyncStorageStrategy<Input['state']>,
|
|
91
|
-
): SignalStoreFeature<Input, AsyncFeatureResult>;
|
|
92
|
-
|
|
93
|
-
// config + localStorage(or sessionStorage)
|
|
94
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
95
|
-
config: SyncConfig<Input['state']>,
|
|
96
|
-
storageStrategy: SyncStorageStrategy<Input['state']>,
|
|
97
|
-
): SignalStoreFeature<Input, SyncFeatureResult>;
|
|
98
|
-
|
|
99
|
-
export function withStorageSync<Input extends SignalStoreFeatureResult>(
|
|
100
|
-
configOrKey: SyncConfig<Input['state']> | string,
|
|
101
|
-
storageStrategy?:
|
|
102
|
-
| AsyncStorageStrategy<Input['state']>
|
|
103
|
-
| SyncStorageStrategy<Input['state']>,
|
|
104
|
-
): SignalStoreFeature<
|
|
105
|
-
Input,
|
|
106
|
-
EmptyFeatureResult & (SyncFeatureResult | AsyncFeatureResult)
|
|
107
|
-
> {
|
|
108
|
-
if (
|
|
109
|
-
typeof configOrKey !== 'string' &&
|
|
110
|
-
configOrKey.storage &&
|
|
111
|
-
storageStrategy
|
|
112
|
-
) {
|
|
113
|
-
throw new Error(
|
|
114
|
-
'You can either pass a storage strategy or a config with storage, but not both.',
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
const config: Required<SyncConfig<Input['state']>> = {
|
|
118
|
-
autoSync: true,
|
|
119
|
-
select: (state: Input['state']) => state,
|
|
120
|
-
parse: JSON.parse,
|
|
121
|
-
stringify: JSON.stringify,
|
|
122
|
-
storage: () => localStorage,
|
|
123
|
-
...(typeof configOrKey === 'string' ? { key: configOrKey } : configOrKey),
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const factory =
|
|
127
|
-
storageStrategy ??
|
|
128
|
-
(config.storage() === localStorage
|
|
129
|
-
? withLocalStorage()
|
|
130
|
-
: withSessionStorage());
|
|
131
|
-
|
|
132
|
-
if (factory.type === 'sync') {
|
|
133
|
-
return createSyncStorageSync(factory, config);
|
|
134
|
-
} else {
|
|
135
|
-
return createAsyncStorageSync(factory, config);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function createSyncStorageSync(
|
|
140
|
-
factory: SyncStorageStrategy<object>,
|
|
141
|
-
config: Required<SyncConfig<object>>,
|
|
142
|
-
) {
|
|
143
|
-
return signalStoreFeature(
|
|
144
|
-
withMethods((store, platformId = inject(PLATFORM_ID)) => {
|
|
145
|
-
return factory(config, store, isPlatformServer(platformId));
|
|
146
|
-
}),
|
|
147
|
-
withHooks({
|
|
148
|
-
onInit(store, platformId = inject(PLATFORM_ID)) {
|
|
149
|
-
if (isPlatformServer(platformId)) {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (config.autoSync) {
|
|
154
|
-
store.readFromStorage();
|
|
155
|
-
watchState(store, () => store.writeToStorage());
|
|
156
|
-
}
|
|
157
|
-
},
|
|
158
|
-
}),
|
|
159
|
-
) satisfies SignalStoreFeature<EmptyFeatureResult, SyncFeatureResult>;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function createAsyncStorageSync(
|
|
163
|
-
factory: AsyncStorageStrategy<object>,
|
|
164
|
-
config: Required<SyncConfig<object>>,
|
|
165
|
-
) {
|
|
166
|
-
return signalStoreFeature(
|
|
167
|
-
withProps(() => {
|
|
168
|
-
const props = {
|
|
169
|
-
/*
|
|
170
|
-
// we need to have that as property (and not state)
|
|
171
|
-
// Otherwise the state watcher fires when updating the sync status
|
|
172
|
-
*/
|
|
173
|
-
[SYNC_STATUS]: signal<'idle' | 'syncing' | 'synced'>('idle'),
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const resolves = [] as (() => void)[];
|
|
177
|
-
|
|
178
|
-
effect(() => {
|
|
179
|
-
const syncStatus = props[SYNC_STATUS]();
|
|
180
|
-
if (syncStatus === 'synced') {
|
|
181
|
-
resolves.forEach((resolve) => resolve());
|
|
182
|
-
resolves.splice(0, resolves.length);
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
...props,
|
|
188
|
-
isSynced: computed(() => props[SYNC_STATUS]() === 'synced'),
|
|
189
|
-
whenSynced: () =>
|
|
190
|
-
new Promise<void>((resolve) => {
|
|
191
|
-
if (props[SYNC_STATUS]() === 'synced') {
|
|
192
|
-
resolve();
|
|
193
|
-
} else {
|
|
194
|
-
resolves.push(resolve);
|
|
195
|
-
}
|
|
196
|
-
}),
|
|
197
|
-
};
|
|
198
|
-
}),
|
|
199
|
-
withMethods((store, platformId = inject(PLATFORM_ID)) => {
|
|
200
|
-
return factory(config, store, isPlatformServer(platformId));
|
|
201
|
-
}),
|
|
202
|
-
withHooks({
|
|
203
|
-
async onInit(store, platformId = inject(PLATFORM_ID)) {
|
|
204
|
-
if (isPlatformServer(platformId)) {
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const initialState = JSON.stringify(getState(store));
|
|
209
|
-
if (config.autoSync) {
|
|
210
|
-
let startWatching = false;
|
|
211
|
-
watchState(store, () => {
|
|
212
|
-
if (!startWatching) {
|
|
213
|
-
const currentState = JSON.stringify(getState(store));
|
|
214
|
-
|
|
215
|
-
// Necessary because getState returns always a new object
|
|
216
|
-
if (currentState === initialState) {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
console.warn(
|
|
221
|
-
`Writing to Store (${config.key}) happened before the state was initially read from storage.`,
|
|
222
|
-
'Please ensure that the store is not in syncing state via `store.whenSynced()` before writing to the state.',
|
|
223
|
-
'Alternatively, you can disable autoSync by passing `autoSync: false` in the config.',
|
|
224
|
-
);
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
return store.writeToStorage();
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
await store.readFromStorage();
|
|
231
|
-
startWatching = true;
|
|
232
|
-
}
|
|
233
|
-
},
|
|
234
|
-
}),
|
|
235
|
-
) satisfies SignalStoreFeature<EmptyFeatureResult, AsyncFeatureResult>;
|
|
236
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { patchState, signalStore } from '@ngrx/signals';
|
|
2
|
-
import { setLoaded, setLoading, withCallState } from './with-call-state';
|
|
3
|
-
|
|
4
|
-
describe('withCallState', () => {
|
|
5
|
-
it('should use and update a callState', () => {
|
|
6
|
-
const DataStore = signalStore({ protectedState: false }, withCallState());
|
|
7
|
-
const dataStore = new DataStore();
|
|
8
|
-
|
|
9
|
-
patchState(dataStore, setLoading());
|
|
10
|
-
|
|
11
|
-
expect(dataStore.callState()).toBe('loading');
|
|
12
|
-
expect(dataStore.loading()).toBe(true);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should use the callState for a collection', () => {
|
|
16
|
-
const DataStore = signalStore(
|
|
17
|
-
{ protectedState: false },
|
|
18
|
-
withCallState({ collection: 'entities' }),
|
|
19
|
-
);
|
|
20
|
-
const dataStore = new DataStore();
|
|
21
|
-
|
|
22
|
-
patchState(dataStore, setLoaded('entities'));
|
|
23
|
-
|
|
24
|
-
expect(dataStore.entitiesCallState()).toBe('loaded');
|
|
25
|
-
expect(dataStore.entitiesLoaded()).toBe(true);
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should use the callState for multiple collections with an array', () => {
|
|
29
|
-
const DataStore = signalStore(
|
|
30
|
-
{ protectedState: false },
|
|
31
|
-
withCallState({ collections: ['entities', 'products'] }),
|
|
32
|
-
);
|
|
33
|
-
const dataStore = new DataStore();
|
|
34
|
-
|
|
35
|
-
patchState(dataStore, setLoaded('entities'), setLoaded('products'));
|
|
36
|
-
|
|
37
|
-
expect(dataStore.entitiesCallState()).toBe('loaded');
|
|
38
|
-
expect(dataStore.productsCallState()).toBe('loaded');
|
|
39
|
-
expect(dataStore.entitiesLoaded()).toBe(true);
|
|
40
|
-
expect(dataStore.productsLoaded()).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
});
|