@angular-architects/ngrx-toolkit 19.4.0 → 19.4.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/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 +2108 -0
- package/fesm2022/angular-architects-ngrx-toolkit.mjs.map +1 -0
- package/{src/index.ts → index.d.ts} +5 -32
- package/lib/assertions/assertions.d.ts +2 -0
- package/{src/lib/devtools/features/with-disabled-name-indicies.ts → lib/devtools/features/with-disabled-name-indicies.d.ts} +1 -5
- package/{src/lib/devtools/features/with-glitch-tracking.ts → lib/devtools/features/with-glitch-tracking.d.ts} +1 -6
- package/{src/lib/devtools/features/with-mapper.ts → lib/devtools/features/with-mapper.d.ts} +1 -7
- package/lib/devtools/internal/current-action-names.d.ts +1 -0
- package/lib/devtools/internal/default-tracker.d.ts +13 -0
- package/lib/devtools/internal/devtools-feature.d.ts +24 -0
- package/lib/devtools/internal/devtools-syncer.service.d.ts +39 -0
- package/lib/devtools/internal/glitch-tracker.service.d.ts +18 -0
- package/lib/devtools/internal/models.d.ts +24 -0
- package/{src/lib/devtools/provide-devtools-config.ts → lib/devtools/provide-devtools-config.d.ts} +4 -16
- package/lib/devtools/rename-devtools-name.d.ts +7 -0
- package/lib/devtools/update-state.d.ts +15 -0
- package/{src/lib/devtools/with-dev-tools-stub.ts → lib/devtools/with-dev-tools-stub.d.ts} +1 -2
- package/lib/devtools/with-devtools.d.ts +24 -0
- package/lib/flattening-operator.d.ts +14 -0
- package/lib/immutable-state/deep-freeze.d.ts +11 -0
- package/lib/immutable-state/is-dev-mode.d.ts +1 -0
- package/lib/immutable-state/with-immutable-state.d.ts +60 -0
- package/lib/mutation/http-mutation.d.ts +86 -0
- package/lib/mutation/mutation.d.ts +20 -0
- package/lib/mutation/rx-mutation.d.ts +76 -0
- package/{src/lib/shared/signal-store-models.ts → lib/shared/signal-store-models.d.ts} +4 -8
- package/lib/shared/throw-if-null.d.ts +1 -0
- package/lib/storage-sync/features/with-indexed-db.d.ts +2 -0
- package/lib/storage-sync/features/with-local-storage.d.ts +3 -0
- package/lib/storage-sync/internal/indexeddb.service.d.ts +29 -0
- package/lib/storage-sync/internal/local-storage.service.d.ts +8 -0
- package/lib/storage-sync/internal/models.d.ts +45 -0
- package/lib/storage-sync/internal/session-storage.service.d.ts +8 -0
- package/lib/storage-sync/with-storage-sync.d.ts +45 -0
- package/lib/with-call-state.d.ts +58 -0
- package/{src/lib/with-conditional.ts → lib/with-conditional.d.ts} +7 -31
- package/lib/with-data-service.d.ts +109 -0
- package/{src/lib/with-feature-factory.ts → lib/with-feature-factory.d.ts} +4 -32
- package/lib/with-mutations.d.ts +51 -0
- package/lib/with-pagination.d.ts +98 -0
- package/lib/with-redux.d.ts +147 -0
- package/lib/with-reset.d.ts +29 -0
- package/lib/with-undo-redo.d.ts +31 -0
- package/package.json +21 -4
- package/redux-connector/index.d.ts +2 -0
- package/redux-connector/src/lib/create-redux.d.ts +13 -0
- package/redux-connector/src/lib/model.d.ts +40 -0
- package/redux-connector/src/lib/rxjs-interop/redux-method.d.ts +14 -0
- package/redux-connector/src/lib/signal-redux-store.d.ts +11 -0
- package/redux-connector/src/lib/util.d.ts +5 -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/lib/assertions/assertions.ts +0 -9
- 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/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-devtools.ts +0 -81
- package/src/lib/flattening-operator.ts +0 -42
- 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 -260
- package/src/lib/immutable-state/with-immutable-state.ts +0 -115
- package/src/lib/mutation/http-mutation.spec.ts +0 -473
- package/src/lib/mutation/http-mutation.ts +0 -172
- package/src/lib/mutation/mutation.ts +0 -26
- package/src/lib/mutation/rx-mutation.spec.ts +0 -594
- package/src/lib/mutation/rx-mutation.ts +0 -208
- package/src/lib/shared/prettify.ts +0 -3
- 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 -308
- package/src/lib/storage-sync/tests/with-storage-sync.spec.ts +0 -268
- package/src/lib/storage-sync/with-storage-sync.ts +0 -233
- 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-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-mutations.spec.ts +0 -537
- package/src/lib/with-mutations.ts +0 -146
- package/src/lib/with-pagination.spec.ts +0 -90
- package/src/lib/with-pagination.ts +0 -353
- 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 -287
- package/src/lib/with-undo-redo.ts +0 -199
- 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 -17
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createEnvironmentInjector,
|
|
3
|
-
EnvironmentInjector,
|
|
4
|
-
inject,
|
|
5
|
-
runInInjectionContext,
|
|
6
|
-
} from '@angular/core';
|
|
7
|
-
import { TestBed } from '@angular/core/testing';
|
|
8
|
-
import { signalStore, withState } from '@ngrx/signals';
|
|
9
|
-
import { withDisabledNameIndices } from '../features/with-disabled-name-indicies';
|
|
10
|
-
import { renameDevtoolsName } from '../rename-devtools-name';
|
|
11
|
-
import { withDevtools } from '../with-devtools';
|
|
12
|
-
import { setupExtensions } from './helpers.spec';
|
|
13
|
-
|
|
14
|
-
describe('withDevtools / renaming', () => {
|
|
15
|
-
it('should automatically index multiple instances', () => {
|
|
16
|
-
const { sendSpy } = setupExtensions();
|
|
17
|
-
const Store = signalStore(
|
|
18
|
-
{ providedIn: 'root' },
|
|
19
|
-
withDevtools('flights'),
|
|
20
|
-
withState({ airline: 'Lufthansa' }),
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
const childContext = createEnvironmentInjector(
|
|
24
|
-
[Store],
|
|
25
|
-
TestBed.inject(EnvironmentInjector),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
TestBed.inject(Store);
|
|
29
|
-
runInInjectionContext(childContext, () => inject(Store));
|
|
30
|
-
|
|
31
|
-
TestBed.flushEffects();
|
|
32
|
-
|
|
33
|
-
expect(sendSpy).lastCalledWith(
|
|
34
|
-
{ type: 'Store Update' },
|
|
35
|
-
{
|
|
36
|
-
flights: { airline: 'Lufthansa' },
|
|
37
|
-
'flights-1': { airline: 'Lufthansa' },
|
|
38
|
-
},
|
|
39
|
-
);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('not index, if multiple instances do not exist simultaneously', () => {
|
|
43
|
-
const { sendSpy } = setupExtensions();
|
|
44
|
-
const Store = signalStore(
|
|
45
|
-
withDevtools('flights'),
|
|
46
|
-
withState({ airline: 'Lufthansa' }),
|
|
47
|
-
);
|
|
48
|
-
|
|
49
|
-
const envInjector = TestBed.inject(EnvironmentInjector);
|
|
50
|
-
const childContext1 = createEnvironmentInjector([Store], envInjector);
|
|
51
|
-
const childContext2 = createEnvironmentInjector([Store], envInjector);
|
|
52
|
-
|
|
53
|
-
runInInjectionContext(childContext1, () => inject(Store));
|
|
54
|
-
TestBed.flushEffects();
|
|
55
|
-
childContext1.destroy();
|
|
56
|
-
|
|
57
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
58
|
-
[
|
|
59
|
-
{ type: 'Store Update' },
|
|
60
|
-
{
|
|
61
|
-
flights: { airline: 'Lufthansa' },
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
runInInjectionContext(childContext2, () => inject(Store));
|
|
67
|
-
TestBed.flushEffects();
|
|
68
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
69
|
-
[
|
|
70
|
-
{ type: 'Store Update' },
|
|
71
|
-
{
|
|
72
|
-
flights: { airline: 'Lufthansa' },
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
{ type: 'Store Update' },
|
|
77
|
-
{
|
|
78
|
-
flights: { airline: 'Lufthansa' },
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
]);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should throw if automatic indexing is disabled', () => {
|
|
85
|
-
setupExtensions();
|
|
86
|
-
const Store = signalStore(
|
|
87
|
-
{ providedIn: 'root' },
|
|
88
|
-
withDevtools('flights', withDisabledNameIndices()),
|
|
89
|
-
withState({ airline: 'Lufthansa' }),
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const childContext = createEnvironmentInjector(
|
|
93
|
-
[Store],
|
|
94
|
-
TestBed.inject(EnvironmentInjector),
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
TestBed.inject(Store);
|
|
98
|
-
expect(() =>
|
|
99
|
-
runInInjectionContext(childContext, () => inject(Store)),
|
|
100
|
-
).toThrow(
|
|
101
|
-
`An instance of the store flights already exists. \
|
|
102
|
-
Enable automatic indexing via withDevTools('flights', { indexNames: true }), or rename it upon instantiation.`,
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should index for two different stores with same devtools name', () => {
|
|
107
|
-
const { sendSpy } = setupExtensions();
|
|
108
|
-
|
|
109
|
-
TestBed.inject(
|
|
110
|
-
signalStore({ providedIn: 'root' }, withDevtools('flights')),
|
|
111
|
-
);
|
|
112
|
-
TestBed.inject(
|
|
113
|
-
signalStore({ providedIn: 'root' }, withDevtools('flights')),
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
TestBed.flushEffects();
|
|
117
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
118
|
-
[
|
|
119
|
-
{ type: 'Store Update' },
|
|
120
|
-
{
|
|
121
|
-
flights: {},
|
|
122
|
-
'flights-1': {},
|
|
123
|
-
},
|
|
124
|
-
],
|
|
125
|
-
]);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should throw for two different stores when indexing is disabled', () => {
|
|
129
|
-
setupExtensions();
|
|
130
|
-
|
|
131
|
-
TestBed.inject(
|
|
132
|
-
signalStore({ providedIn: 'root' }, withDevtools('flights')),
|
|
133
|
-
);
|
|
134
|
-
expect(() =>
|
|
135
|
-
TestBed.inject(
|
|
136
|
-
signalStore(
|
|
137
|
-
{ providedIn: 'root' },
|
|
138
|
-
withDevtools('flights', withDisabledNameIndices()),
|
|
139
|
-
),
|
|
140
|
-
),
|
|
141
|
-
).toThrow();
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should not throw for two different stores if only the first one has indexing disabled', () => {
|
|
145
|
-
setupExtensions();
|
|
146
|
-
|
|
147
|
-
TestBed.inject(
|
|
148
|
-
signalStore(
|
|
149
|
-
{ providedIn: 'root' },
|
|
150
|
-
withDevtools('flights', withDisabledNameIndices()),
|
|
151
|
-
),
|
|
152
|
-
);
|
|
153
|
-
expect(() =>
|
|
154
|
-
TestBed.inject(
|
|
155
|
-
signalStore({ providedIn: 'root' }, withDevtools('flights')),
|
|
156
|
-
),
|
|
157
|
-
).not.toThrow();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
describe('renaming', () => {
|
|
161
|
-
it('should allow to rename the store before first sync', () => {
|
|
162
|
-
const { sendSpy } = setupExtensions();
|
|
163
|
-
|
|
164
|
-
const Store = signalStore(
|
|
165
|
-
{ providedIn: 'root' },
|
|
166
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
167
|
-
withDevtools('flight'),
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
const store = TestBed.inject(Store);
|
|
171
|
-
renameDevtoolsName(store, 'flights');
|
|
172
|
-
TestBed.flushEffects();
|
|
173
|
-
|
|
174
|
-
expect(sendSpy).toHaveBeenCalledWith(
|
|
175
|
-
{ type: 'Store Update' },
|
|
176
|
-
{ flights: { name: 'Product', price: 10.5 } },
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should throw on rename if name already exists', () => {
|
|
181
|
-
setupExtensions();
|
|
182
|
-
const Store1 = signalStore(
|
|
183
|
-
{ providedIn: 'root' },
|
|
184
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
185
|
-
withDevtools('shop'),
|
|
186
|
-
);
|
|
187
|
-
|
|
188
|
-
const Store2 = signalStore(
|
|
189
|
-
{ providedIn: 'root' },
|
|
190
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
191
|
-
withDevtools('mall'),
|
|
192
|
-
);
|
|
193
|
-
TestBed.inject(Store1);
|
|
194
|
-
const store = TestBed.inject(Store2);
|
|
195
|
-
TestBed.flushEffects();
|
|
196
|
-
|
|
197
|
-
expect(() => renameDevtoolsName(store, 'shop')).toThrow(
|
|
198
|
-
'NgRx Toolkit/DevTools: cannot rename from mall to shop. shop is already assigned to another SignalStore instance.',
|
|
199
|
-
);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it('should throw if applied to a SignalStore without DevTools', () => {
|
|
203
|
-
setupExtensions();
|
|
204
|
-
const Store = signalStore(
|
|
205
|
-
{ providedIn: 'root' },
|
|
206
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
207
|
-
);
|
|
208
|
-
|
|
209
|
-
const store = TestBed.inject(Store);
|
|
210
|
-
|
|
211
|
-
expect(() => renameDevtoolsName(store, 'shop')).toThrow(
|
|
212
|
-
"Devtools extensions haven't been added to this store.",
|
|
213
|
-
);
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { DevtoolsSyncer } from '../internal/devtools-syncer.service';
|
|
3
|
-
import { provideDevtoolsConfig } from '../provide-devtools-config';
|
|
4
|
-
import { setupExtensions } from './helpers.spec';
|
|
5
|
-
|
|
6
|
-
describe('provideDevtoolsConfig', () => {
|
|
7
|
-
it('DevtoolsSyncer should use the default configuration if none is provided', () => {
|
|
8
|
-
const { connectSpy } = setupExtensions();
|
|
9
|
-
TestBed.inject(DevtoolsSyncer);
|
|
10
|
-
expect(connectSpy).toHaveBeenCalledWith({
|
|
11
|
-
name: 'NgRx SignalStore',
|
|
12
|
-
});
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('DevtoolsSyncer should use the configuration provided', () => {
|
|
16
|
-
const { connectSpy } = setupExtensions();
|
|
17
|
-
TestBed.configureTestingModule({
|
|
18
|
-
providers: [provideDevtoolsConfig({ name: 'test' })],
|
|
19
|
-
});
|
|
20
|
-
TestBed.inject(DevtoolsSyncer);
|
|
21
|
-
expect(connectSpy).toHaveBeenCalledWith({
|
|
22
|
-
name: 'test',
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { computed } from '@angular/core';
|
|
2
|
-
import { patchState, signalStore, withState } from '@ngrx/signals';
|
|
3
|
-
import { withDevtools } from '../with-devtools';
|
|
4
|
-
|
|
5
|
-
it('should compile when signalStore is extended from', () => {
|
|
6
|
-
class CounterStore extends signalStore(
|
|
7
|
-
{ protectedState: false },
|
|
8
|
-
withState({ count: 0 }),
|
|
9
|
-
withDevtools('counter-store'),
|
|
10
|
-
) {
|
|
11
|
-
readonly myReadonlyProp = 42;
|
|
12
|
-
|
|
13
|
-
readonly doubleCount = computed(() => this.count() * 2);
|
|
14
|
-
|
|
15
|
-
increment(): void {
|
|
16
|
-
patchState(this, { count: this.count() + 1 });
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
});
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { signalStore, withMethods } from '@ngrx/signals';
|
|
2
|
-
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
|
|
3
|
-
import { setLoaded, withCallState } from '../../with-call-state';
|
|
4
|
-
import { updateState } from '../update-state';
|
|
5
|
-
|
|
6
|
-
describe('updateState', () => {
|
|
7
|
-
it('should work with multiple updaters', () => {
|
|
8
|
-
interface Item {
|
|
9
|
-
id: string;
|
|
10
|
-
name: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
signalStore(
|
|
14
|
-
withEntities<Item>(),
|
|
15
|
-
withCallState({ collection: 'items' }),
|
|
16
|
-
withMethods((store) => ({
|
|
17
|
-
loadItems() {
|
|
18
|
-
// This should not cause a type error
|
|
19
|
-
updateState(
|
|
20
|
-
store,
|
|
21
|
-
'Items loaded successfully',
|
|
22
|
-
setAllEntities([] as Item[]),
|
|
23
|
-
setLoaded('items'),
|
|
24
|
-
);
|
|
25
|
-
},
|
|
26
|
-
})),
|
|
27
|
-
);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
@@ -1,272 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createEnvironmentInjector,
|
|
3
|
-
EnvironmentInjector,
|
|
4
|
-
inject,
|
|
5
|
-
runInInjectionContext,
|
|
6
|
-
} from '@angular/core';
|
|
7
|
-
import { TestBed } from '@angular/core/testing';
|
|
8
|
-
import { patchState, signalStore, withMethods, withState } from '@ngrx/signals';
|
|
9
|
-
import { withGlitchTracking } from '../features/with-glitch-tracking';
|
|
10
|
-
import { renameDevtoolsName } from '../rename-devtools-name';
|
|
11
|
-
import { withDevtools } from '../with-devtools';
|
|
12
|
-
import { setupExtensions } from './helpers.spec';
|
|
13
|
-
|
|
14
|
-
describe('withGlitchTracking', () => {
|
|
15
|
-
it('should sync immediately upon instantiation', () => {
|
|
16
|
-
const { sendSpy } = setupExtensions();
|
|
17
|
-
|
|
18
|
-
const Store = signalStore(
|
|
19
|
-
{ providedIn: 'root' },
|
|
20
|
-
withDevtools('counter', withGlitchTracking()),
|
|
21
|
-
withState({ count: 0 }),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
expect(sendSpy).not.toHaveBeenCalled();
|
|
25
|
-
TestBed.inject(Store);
|
|
26
|
-
|
|
27
|
-
expect(sendSpy).toHaveBeenCalledWith(
|
|
28
|
-
{ type: 'Store Update' },
|
|
29
|
-
{ counter: { count: 0 } },
|
|
30
|
-
);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should sync synchronous state changes', () => {
|
|
34
|
-
const { sendSpy } = setupExtensions();
|
|
35
|
-
|
|
36
|
-
const Store = signalStore(
|
|
37
|
-
{ providedIn: 'root' },
|
|
38
|
-
withState({ count: 0 }),
|
|
39
|
-
withDevtools('counter', withGlitchTracking()),
|
|
40
|
-
withMethods((store) => ({
|
|
41
|
-
increase: () =>
|
|
42
|
-
patchState(store, (value) => ({ count: value.count + 1 })),
|
|
43
|
-
})),
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
const store = TestBed.inject(Store);
|
|
47
|
-
|
|
48
|
-
store.increase();
|
|
49
|
-
store.increase();
|
|
50
|
-
store.increase();
|
|
51
|
-
TestBed.flushEffects();
|
|
52
|
-
|
|
53
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
54
|
-
[{ type: 'Store Update' }, { counter: { count: 0 } }],
|
|
55
|
-
[{ type: 'Store Update' }, { counter: { count: 1 } }],
|
|
56
|
-
[{ type: 'Store Update' }, { counter: { count: 2 } }],
|
|
57
|
-
[{ type: 'Store Update' }, { counter: { count: 3 } }],
|
|
58
|
-
]);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should support a mixed approach', () => {
|
|
62
|
-
const { sendSpy } = setupExtensions();
|
|
63
|
-
|
|
64
|
-
const GlitchFreeStore = signalStore(
|
|
65
|
-
{ providedIn: 'root' },
|
|
66
|
-
withState({ count: 0 }),
|
|
67
|
-
withDevtools('glitch-free counter'),
|
|
68
|
-
withMethods((store) => ({
|
|
69
|
-
increase: () =>
|
|
70
|
-
patchState(store, (value) => ({ count: value.count + 1 })),
|
|
71
|
-
})),
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
const GlitchStore = signalStore(
|
|
75
|
-
{ providedIn: 'root' },
|
|
76
|
-
withState({ count: 0 }),
|
|
77
|
-
withDevtools('glitch counter', withGlitchTracking()),
|
|
78
|
-
withMethods((store) => ({
|
|
79
|
-
increase: () =>
|
|
80
|
-
patchState(store, (value) => ({ count: value.count + 1 })),
|
|
81
|
-
})),
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
const glitchFreeStore = TestBed.inject(GlitchFreeStore);
|
|
85
|
-
const glitchStore = TestBed.inject(GlitchStore);
|
|
86
|
-
|
|
87
|
-
TestBed.flushEffects();
|
|
88
|
-
for (let i = 0; i < 2; i++) {
|
|
89
|
-
glitchFreeStore.increase();
|
|
90
|
-
glitchStore.increase();
|
|
91
|
-
}
|
|
92
|
-
TestBed.flushEffects();
|
|
93
|
-
|
|
94
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
95
|
-
[{ type: 'Store Update' }, { 'glitch counter': { count: 0 } }],
|
|
96
|
-
[
|
|
97
|
-
{ type: 'Store Update' },
|
|
98
|
-
{ 'glitch-free counter': { count: 0 }, 'glitch counter': { count: 0 } },
|
|
99
|
-
],
|
|
100
|
-
[
|
|
101
|
-
{ type: 'Store Update' },
|
|
102
|
-
{ 'glitch-free counter': { count: 0 }, 'glitch counter': { count: 1 } },
|
|
103
|
-
],
|
|
104
|
-
[
|
|
105
|
-
{ type: 'Store Update' },
|
|
106
|
-
{ 'glitch-free counter': { count: 0 }, 'glitch counter': { count: 2 } },
|
|
107
|
-
],
|
|
108
|
-
[
|
|
109
|
-
{ type: 'Store Update' },
|
|
110
|
-
{ 'glitch-free counter': { count: 2 }, 'glitch counter': { count: 2 } },
|
|
111
|
-
],
|
|
112
|
-
]);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('two glitch stores should sync per change', () => {
|
|
116
|
-
const { sendSpy } = setupExtensions();
|
|
117
|
-
|
|
118
|
-
const GlitchStore1 = signalStore(
|
|
119
|
-
{ providedIn: 'root' },
|
|
120
|
-
withState({ count: 0 }),
|
|
121
|
-
withDevtools('glitch counter 1', withGlitchTracking()),
|
|
122
|
-
withMethods((store) => ({
|
|
123
|
-
increase: () =>
|
|
124
|
-
patchState(store, (value) => ({ count: value.count + 1 })),
|
|
125
|
-
})),
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
const GlitchStore2 = signalStore(
|
|
129
|
-
{ providedIn: 'root' },
|
|
130
|
-
withState({ count: 0 }),
|
|
131
|
-
withDevtools('glitch counter 2', withGlitchTracking()),
|
|
132
|
-
withMethods((store) => ({
|
|
133
|
-
increase: () =>
|
|
134
|
-
patchState(store, (value) => ({ count: value.count + 1 })),
|
|
135
|
-
})),
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const glitchStore1 = TestBed.inject(GlitchStore1);
|
|
139
|
-
const glitchStore2 = TestBed.inject(GlitchStore2);
|
|
140
|
-
|
|
141
|
-
for (let i = 0; i < 2; i++) {
|
|
142
|
-
glitchStore1.increase();
|
|
143
|
-
glitchStore2.increase();
|
|
144
|
-
}
|
|
145
|
-
TestBed.flushEffects();
|
|
146
|
-
|
|
147
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
148
|
-
[{ type: 'Store Update' }, { 'glitch counter 1': { count: 0 } }],
|
|
149
|
-
[
|
|
150
|
-
{ type: 'Store Update' },
|
|
151
|
-
{ 'glitch counter 1': { count: 0 }, 'glitch counter 2': { count: 0 } },
|
|
152
|
-
],
|
|
153
|
-
[
|
|
154
|
-
{ type: 'Store Update' },
|
|
155
|
-
{ 'glitch counter 1': { count: 1 }, 'glitch counter 2': { count: 0 } },
|
|
156
|
-
],
|
|
157
|
-
[
|
|
158
|
-
{ type: 'Store Update' },
|
|
159
|
-
{ 'glitch counter 1': { count: 1 }, 'glitch counter 2': { count: 1 } },
|
|
160
|
-
],
|
|
161
|
-
[
|
|
162
|
-
{ type: 'Store Update' },
|
|
163
|
-
{ 'glitch counter 1': { count: 2 }, 'glitch counter 2': { count: 1 } },
|
|
164
|
-
],
|
|
165
|
-
[
|
|
166
|
-
{ type: 'Store Update' },
|
|
167
|
-
{ 'glitch counter 1': { count: 2 }, 'glitch counter 2': { count: 2 } },
|
|
168
|
-
],
|
|
169
|
-
]);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
it('should not sync glitch-free if glitched is renamed', () => {
|
|
173
|
-
const { sendSpy } = setupExtensions();
|
|
174
|
-
|
|
175
|
-
const GlitchFreeStore = signalStore(
|
|
176
|
-
{ providedIn: 'root' },
|
|
177
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
178
|
-
withDevtools('flight1'),
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
const GlitchStore = signalStore(
|
|
182
|
-
{ providedIn: 'root' },
|
|
183
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
184
|
-
withDevtools('flight2', withGlitchTracking()),
|
|
185
|
-
);
|
|
186
|
-
|
|
187
|
-
TestBed.inject(GlitchFreeStore);
|
|
188
|
-
const glitchStore = TestBed.inject(GlitchStore);
|
|
189
|
-
|
|
190
|
-
TestBed.flushEffects();
|
|
191
|
-
|
|
192
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
193
|
-
[{ type: 'Store Update' }, { flight2: { name: 'Product', price: 10.5 } }],
|
|
194
|
-
[
|
|
195
|
-
{ type: 'Store Update' },
|
|
196
|
-
{
|
|
197
|
-
flight1: { name: 'Product', price: 10.5 },
|
|
198
|
-
flight2: { name: 'Product', price: 10.5 },
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
]);
|
|
202
|
-
sendSpy.mockClear();
|
|
203
|
-
|
|
204
|
-
renameDevtoolsName(glitchStore, 'flights2');
|
|
205
|
-
TestBed.flushEffects();
|
|
206
|
-
|
|
207
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
208
|
-
[
|
|
209
|
-
{ type: 'Store Update' },
|
|
210
|
-
{
|
|
211
|
-
flight1: { name: 'Product', price: 10.5 },
|
|
212
|
-
flights2: { name: 'Product', price: 10.5 },
|
|
213
|
-
},
|
|
214
|
-
],
|
|
215
|
-
]);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('should not sync glitch tracker if glitch-free store is renamed', () => {
|
|
219
|
-
const { sendSpy } = setupExtensions();
|
|
220
|
-
|
|
221
|
-
const GlitchFreeStore = signalStore(
|
|
222
|
-
{ providedIn: 'root' },
|
|
223
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
224
|
-
withDevtools('flight1'),
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
const GlitchStore = signalStore(
|
|
228
|
-
{ providedIn: 'root' },
|
|
229
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
230
|
-
withDevtools('glitched Flights', withGlitchTracking()),
|
|
231
|
-
);
|
|
232
|
-
|
|
233
|
-
const glitchFreeStore = TestBed.inject(GlitchFreeStore);
|
|
234
|
-
TestBed.inject(GlitchStore);
|
|
235
|
-
TestBed.flushEffects();
|
|
236
|
-
|
|
237
|
-
sendSpy.mockClear();
|
|
238
|
-
renameDevtoolsName(glitchFreeStore, 'glitch-free Flights');
|
|
239
|
-
expect(sendSpy).not.toHaveBeenCalled();
|
|
240
|
-
TestBed.flushEffects();
|
|
241
|
-
|
|
242
|
-
expect(sendSpy.mock.calls).toEqual([
|
|
243
|
-
[
|
|
244
|
-
{ type: 'Store Update' },
|
|
245
|
-
{
|
|
246
|
-
'glitch-free Flights': { name: 'Product', price: 10.5 },
|
|
247
|
-
'glitched Flights': { name: 'Product', price: 10.5 },
|
|
248
|
-
},
|
|
249
|
-
],
|
|
250
|
-
]);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should destroy watcher if store is destroyed', () => {
|
|
254
|
-
const { sendSpy } = setupExtensions();
|
|
255
|
-
|
|
256
|
-
const GlitchStore = signalStore(
|
|
257
|
-
withState({ name: 'Product', price: 10.5 }),
|
|
258
|
-
withDevtools('Glitched Store', withGlitchTracking()),
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
const childContext = createEnvironmentInjector(
|
|
262
|
-
[GlitchStore],
|
|
263
|
-
TestBed.inject(EnvironmentInjector),
|
|
264
|
-
);
|
|
265
|
-
runInInjectionContext(childContext, () => inject(GlitchStore));
|
|
266
|
-
|
|
267
|
-
expect(sendSpy).toHaveBeenCalled();
|
|
268
|
-
sendSpy.mockClear();
|
|
269
|
-
childContext.destroy();
|
|
270
|
-
expect(sendSpy).toHaveBeenCalledWith({ type: 'Store Update' }, {});
|
|
271
|
-
});
|
|
272
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
import { signalStore, withState } from '@ngrx/signals';
|
|
3
|
-
import { withMapper } from '../features/with-mapper';
|
|
4
|
-
import { withDevtools } from '../with-devtools';
|
|
5
|
-
import { setupExtensions } from './helpers.spec';
|
|
6
|
-
|
|
7
|
-
function domRemover(state: Record<string, unknown>) {
|
|
8
|
-
return Object.keys(state).reduce((acc, key) => {
|
|
9
|
-
const value = state[key];
|
|
10
|
-
|
|
11
|
-
if (value instanceof HTMLElement) {
|
|
12
|
-
return acc;
|
|
13
|
-
} else {
|
|
14
|
-
return { ...acc, [key]: value };
|
|
15
|
-
}
|
|
16
|
-
}, {});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
describe('with-mapper', () => {
|
|
20
|
-
it('should remove DOM Nodes', () => {
|
|
21
|
-
const { sendSpy } = setupExtensions();
|
|
22
|
-
|
|
23
|
-
const Store = signalStore(
|
|
24
|
-
{ providedIn: 'root' },
|
|
25
|
-
withState({
|
|
26
|
-
name: 'Car',
|
|
27
|
-
carElement: document.createElement('div'),
|
|
28
|
-
}),
|
|
29
|
-
withDevtools('shop', withMapper(domRemover)),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
TestBed.inject(Store);
|
|
33
|
-
TestBed.flushEffects();
|
|
34
|
-
expect(sendSpy).toHaveBeenCalledWith(
|
|
35
|
-
{ type: 'Store Update' },
|
|
36
|
-
{ shop: { name: 'Car' } },
|
|
37
|
-
);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('should every property ending with *Key', () => {
|
|
41
|
-
const { sendSpy } = setupExtensions();
|
|
42
|
-
const Store = signalStore(
|
|
43
|
-
{ providedIn: 'root' },
|
|
44
|
-
withState({
|
|
45
|
-
name: 'Car',
|
|
46
|
-
unlockKey: '1234',
|
|
47
|
-
}),
|
|
48
|
-
withDevtools(
|
|
49
|
-
'shop',
|
|
50
|
-
withMapper((state: Record<string, unknown>) =>
|
|
51
|
-
Object.keys(state).reduce((acc, key) => {
|
|
52
|
-
if (key.endsWith('Key')) {
|
|
53
|
-
return acc;
|
|
54
|
-
} else {
|
|
55
|
-
return { ...acc, [key]: state[key] };
|
|
56
|
-
}
|
|
57
|
-
}, {}),
|
|
58
|
-
),
|
|
59
|
-
),
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
TestBed.inject(Store);
|
|
63
|
-
TestBed.flushEffects();
|
|
64
|
-
expect(sendSpy).toHaveBeenCalledWith(
|
|
65
|
-
{ type: 'Store Update' },
|
|
66
|
-
{ shop: { name: 'Car' } },
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
patchState as originalPatchState,
|
|
3
|
-
PartialStateUpdater,
|
|
4
|
-
WritableStateSource,
|
|
5
|
-
} from '@ngrx/signals';
|
|
6
|
-
import { currentActionNames } from './internal/current-action-names';
|
|
7
|
-
|
|
8
|
-
type PatchFn = typeof originalPatchState extends (
|
|
9
|
-
arg1: infer First,
|
|
10
|
-
...args: infer Rest
|
|
11
|
-
) => infer Returner
|
|
12
|
-
? (state: First, action: string, ...rest: Rest) => Returner
|
|
13
|
-
: never;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* @deprecated Has been renamed to `updateState`
|
|
17
|
-
*/
|
|
18
|
-
export const patchState: PatchFn = (state, action, ...rest) => {
|
|
19
|
-
updateState(state, action, ...rest);
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Wrapper of `patchState` for DevTools integration. Next to updating the state,
|
|
24
|
-
* it also sends the action to the DevTools.
|
|
25
|
-
* @param stateSource state of Signal Store
|
|
26
|
-
* @param action name of action how it will show in DevTools
|
|
27
|
-
* @param updaters updater functions or objects
|
|
28
|
-
*/
|
|
29
|
-
export function updateState<State extends object>(
|
|
30
|
-
stateSource: WritableStateSource<State>,
|
|
31
|
-
action: string,
|
|
32
|
-
...updaters: Array<
|
|
33
|
-
Partial<NoInfer<State>> | PartialStateUpdater<NoInfer<State>>
|
|
34
|
-
>
|
|
35
|
-
): void {
|
|
36
|
-
currentActionNames.add(action);
|
|
37
|
-
return originalPatchState(stateSource, ...updaters);
|
|
38
|
-
}
|