@angular-architects/ngrx-toolkit 0.0.4 → 0.0.6

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/README.md CHANGED
@@ -1,220 +1,369 @@
1
- # NgRx Toolkit
2
-
3
- <p align="center">
4
- <img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
5
- </p>
6
-
7
- NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
8
-
9
- - Devtools: Integration into Redux Devtools
10
- - Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
11
-
12
- To install it, run
13
-
14
- ```shell
15
- npm i @angular-architects/ngrx-toolkit
16
- ```
17
-
18
- ## Devtools: `withDevtools()`
19
-
20
- This extension is very easy to use. Just add it to a `signalStore`. Example:
21
-
22
- ```typescript
23
- export const FlightStore = signalStore(
24
- { providedIn: 'root' },
25
- withDevtools('flights'), // <-- add this
26
- withState({ flights: [] as Flight[] }),
27
- // ...
28
- );
29
- ```
30
-
31
- ## Redux: `withRedux()`
32
-
33
- `withRedux()` bring back the Redux pattern into the Signal Store.
34
-
35
- It can be combined with any other extension of the Signal Store.
36
-
37
- Example:
38
-
39
- ```typescript
40
- export const FlightStore = signalStore(
41
- { providedIn: 'root' },
42
- withState({ flights: [] as Flight[] }),
43
- withRedux({
44
- actions: {
45
- public: {
46
- load: payload<{ from: string; to: string }>(),
47
- },
48
- private: {
49
- loaded: payload<{ flights: Flight[] }>(),
50
- },
51
- },
52
- reducer(actions, on) {
53
- on(actions.loaded, ({ flights }, state) => {
54
- patchState(state, 'flights loaded', { flights });
55
- });
56
- },
57
- effects(actions, create) {
58
- const httpClient = inject(HttpClient);
59
- return {
60
- load$: create(actions.load).pipe(
61
- switchMap(({ from, to }) =>
62
- httpClient.get<Flight[]>(
63
- 'https://demo.angulararchitects.io/api/flight',
64
- {
65
- params: new HttpParams().set('from', from).set('to', to),
66
- },
67
- ),
68
- ),
69
- tap((flights) => actions.loaded({ flights })),
70
- ),
71
- };
72
- },
73
- }),
74
- );
75
- ```
76
-
77
- ## DataService `withDataService()`
78
-
79
- `withDataService()` allows to connect a Data Service to the store:
80
-
81
- This gives you a store for a CRUD use case:
82
-
83
- ```typescript
84
- export const SimpleFlightBookingStore = signalStore(
85
- { providedIn: 'root' },
86
- withCallState(),
87
- withEntities<Flight>(),
88
- withDataService({
89
- dataServiceType: FlightService,
90
- filter: { from: 'Paris', to: 'New York' },
91
- }),
92
- withUndoRedo(),
93
- );
94
- ```
95
-
96
- The features ``withCallState`` and ``withUndoRedo`` are optional, but when present, they enrich each other.
97
-
98
- The Data Service needs to implement the ``DataService`` interface:
99
-
100
- ```typescript
101
- @Injectable({
102
- providedIn: 'root'
103
- })
104
- export class FlightService implements DataService<Flight, FlightFilter> {
105
- loadById(id: EntityId): Promise<Flight> { ... }
106
- load(filter: FlightFilter): Promise<Flight[]> { ... }
107
-
108
- create(entity: Flight): Promise<Flight> { ... }
109
- update(entity: Flight): Promise<Flight> { ... }
110
- delete(entity: Flight): Promise<void> { ... }
111
- [...]
112
- }
113
- ```
114
-
115
- Once the store is defined, it gives its consumers numerous signals and methods they just need to delegate to:
116
-
117
- ```typescript
118
- @Component(...)
119
- export class FlightSearchSimpleComponent {
120
- private store = inject(SimpleFlightBookingStore);
121
-
122
- from = this.store.filter.from;
123
- to = this.store.filter.to;
124
- flights = this.store.entities;
125
- selected = this.store.selectedEntities;
126
- selectedIds = this.store.selectedIds;
127
-
128
- loading = this.store.loading;
129
-
130
- canUndo = this.store.canUndo;
131
- canRedo = this.store.canRedo;
132
-
133
- async search() {
134
- this.store.load();
135
- }
136
-
137
- undo(): void {
138
- this.store.undo();
139
- }
140
-
141
- redo(): void {
142
- this.store.redo();
143
- }
144
-
145
- updateCriteria(from: string, to: string): void {
146
- this.store.updateFilter({ from, to });
147
- }
148
-
149
- updateBasket(id: number, selected: boolean): void {
150
- this.store.updateSelected(id, selected);
151
- }
152
-
153
- }
154
- ```
155
-
156
- ## DataService with Dynamic Properties
157
-
158
- To avoid naming conflicts, the properties set up by ``withDataService`` and the connected features can be configured in a typesafe way:
159
-
160
- ```typescript
161
- export const FlightBookingStore = signalStore(
162
- { providedIn: 'root' },
163
- withCallState({
164
- collection: 'flight'
165
- }),
166
- withEntities({
167
- entity: type<Flight>(),
168
- collection: 'flight'
169
- }),
170
- withDataService({
171
- dataServiceType: FlightService,
172
- filter: { from: 'Graz', to: 'Hamburg' },
173
- collection: 'flight'
174
- }),
175
- withUndoRedo({
176
- collections: ['flight'],
177
- }),
178
- );
179
- ```
180
-
181
- This setup makes them use ``flight`` as part of the used property names. As these implementations respect the Type Script type system, the compiler will make sure these properties are used in a typesafe way:
182
-
183
- ```typescript
184
- @Component(...)
185
- export class FlightSearchDynamicComponent {
186
- private store = inject(FlightBookingStore);
187
-
188
- from = this.store.flightFilter.from;
189
- to = this.store.flightFilter.to;
190
- flights = this.store.flightEntities;
191
- selected = this.store.selectedFlightEntities;
192
- selectedIds = this.store.selectedFlightIds;
193
-
194
- loading = this.store.flightLoading;
195
-
196
- canUndo = this.store.canUndo;
197
- canRedo = this.store.canRedo;
198
-
199
- async search() {
200
- this.store.loadFlightEntities();
201
- }
202
-
203
- undo(): void {
204
- this.store.undo();
205
- }
206
-
207
- redo(): void {
208
- this.store.redo();
209
- }
210
-
211
- updateCriteria(from: string, to: string): void {
212
- this.store.updateFlightFilter({ from, to });
213
- }
214
-
215
- updateBasket(id: number, selected: boolean): void {
216
- this.store.updateSelectedFlightEntities(id, selected);
217
- }
218
-
219
- }
220
- ```
1
+ # NgRx Toolkit
2
+
3
+ [![npm](https://img.shields.io/npm/v/%40angular-architects%2Fngrx-toolkit.svg)](https://www.npmjs.com/package/%40angular-architects%2Fngrx-toolkit)
4
+
5
+ <p align="center">
6
+ <img src="https://raw.githubusercontent.com/angular-architects/ngrx-toolkit/main/logo.png" width="320" style="text-align: center">
7
+ </p>
8
+
9
+ NgRx Toolkit is an extension to the NgRx Signals Store. **It is still in beta** but already offers following features:
10
+
11
+ - Devtools: Integration into Redux Devtools
12
+ - Redux: Possibility to use the Redux Pattern (Reducer, Actions, Effects)
13
+ - Redux Connector: Map NgRx Store Actions to a present Signal Store
14
+
15
+ To install it, run
16
+
17
+ ```shell
18
+ npm i @angular-architects/ngrx-toolkit
19
+ ```
20
+
21
+
22
+ - [NgRx Toolkit](#ngrx-toolkit)
23
+ - [Devtools: `withDevtools()`](#devtools-withdevtools)
24
+ - [Redux: `withRedux()`](#redux-withredux)
25
+ - [DataService `withDataService()`](#dataservice-withdataservice)
26
+ - [DataService with Dynamic Properties](#dataservice-with-dynamic-properties)
27
+ - [Redux Connector for the NgRx Signal Store `createReduxState()`](#redux-connector-for-the-ngrx-signal-store-createreduxstate)
28
+ - [Use a present Signal Store](#use-a-present-signal-store)
29
+ - [Use well-known NgRx Store Actions](#use-well-known-ngrx-store-actions)
30
+ - [Map Actions to Methods](#map-actions-to-methods)
31
+ - [Register an Angular Dependency Injection Provider](#register-an-angular-dependency-injection-provider)
32
+ - [Use the Store in your Component](#use-the-store-in-your-component)
33
+
34
+
35
+ ## Devtools: `withDevtools()`
36
+
37
+ This extension is very easy to use. Just add it to a `signalStore`. Example:
38
+
39
+ ```typescript
40
+ export const FlightStore = signalStore(
41
+ { providedIn: 'root' },
42
+ withDevtools('flights'), // <-- add this
43
+ withState({ flights: [] as Flight[] }),
44
+ // ...
45
+ );
46
+ ```
47
+
48
+ ## Redux: `withRedux()`
49
+
50
+ `withRedux()` bring back the Redux pattern into the Signal Store.
51
+
52
+ It can be combined with any other extension of the Signal Store.
53
+
54
+ Example:
55
+
56
+ ```typescript
57
+ export const FlightStore = signalStore(
58
+ { providedIn: 'root' },
59
+ withState({ flights: [] as Flight[] }),
60
+ withRedux({
61
+ actions: {
62
+ public: {
63
+ load: payload<{ from: string; to: string }>(),
64
+ },
65
+ private: {
66
+ loaded: payload<{ flights: Flight[] }>(),
67
+ },
68
+ },
69
+ reducer(actions, on) {
70
+ on(actions.loaded, ({ flights }, state) => {
71
+ patchState(state, 'flights loaded', { flights });
72
+ });
73
+ },
74
+ effects(actions, create) {
75
+ const httpClient = inject(HttpClient);
76
+ return {
77
+ load$: create(actions.load).pipe(
78
+ switchMap(({ from, to }) =>
79
+ httpClient.get<Flight[]>(
80
+ 'https://demo.angulararchitects.io/api/flight',
81
+ {
82
+ params: new HttpParams().set('from', from).set('to', to),
83
+ },
84
+ ),
85
+ ),
86
+ tap((flights) => actions.loaded({ flights })),
87
+ ),
88
+ };
89
+ },
90
+ }),
91
+ );
92
+ ```
93
+
94
+ ## DataService `withDataService()`
95
+
96
+ `withDataService()` allows to connect a Data Service to the store:
97
+
98
+ This gives you a store for a CRUD use case:
99
+
100
+ ```typescript
101
+ export const SimpleFlightBookingStore = signalStore(
102
+ { providedIn: 'root' },
103
+ withCallState(),
104
+ withEntities<Flight>(),
105
+ withDataService({
106
+ dataServiceType: FlightService,
107
+ filter: { from: 'Paris', to: 'New York' },
108
+ }),
109
+ withUndoRedo(),
110
+ );
111
+ ```
112
+
113
+ The features ``withCallState`` and ``withUndoRedo`` are optional, but when present, they enrich each other.
114
+
115
+ The Data Service needs to implement the ``DataService`` interface:
116
+
117
+ ```typescript
118
+ @Injectable({
119
+ providedIn: 'root'
120
+ })
121
+ export class FlightService implements DataService<Flight, FlightFilter> {
122
+ loadById(id: EntityId): Promise<Flight> { ... }
123
+ load(filter: FlightFilter): Promise<Flight[]> { ... }
124
+
125
+ create(entity: Flight): Promise<Flight> { ... }
126
+ update(entity: Flight): Promise<Flight> { ... }
127
+ delete(entity: Flight): Promise<void> { ... }
128
+ [...]
129
+ }
130
+ ```
131
+
132
+ Once the store is defined, it gives its consumers numerous signals and methods they just need to delegate to:
133
+
134
+ ```typescript
135
+ @Component(...)
136
+ export class FlightSearchSimpleComponent {
137
+ private store = inject(SimpleFlightBookingStore);
138
+
139
+ from = this.store.filter.from;
140
+ to = this.store.filter.to;
141
+ flights = this.store.entities;
142
+ selected = this.store.selectedEntities;
143
+ selectedIds = this.store.selectedIds;
144
+
145
+ loading = this.store.loading;
146
+
147
+ canUndo = this.store.canUndo;
148
+ canRedo = this.store.canRedo;
149
+
150
+ async search() {
151
+ this.store.load();
152
+ }
153
+
154
+ undo(): void {
155
+ this.store.undo();
156
+ }
157
+
158
+ redo(): void {
159
+ this.store.redo();
160
+ }
161
+
162
+ updateCriteria(from: string, to: string): void {
163
+ this.store.updateFilter({ from, to });
164
+ }
165
+
166
+ updateBasket(id: number, selected: boolean): void {
167
+ this.store.updateSelected(id, selected);
168
+ }
169
+
170
+ }
171
+ ```
172
+
173
+ ## DataService with Dynamic Properties
174
+
175
+ To avoid naming conflicts, the properties set up by ``withDataService`` and the connected features can be configured in a typesafe way:
176
+
177
+ ```typescript
178
+ export const FlightBookingStore = signalStore(
179
+ { providedIn: 'root' },
180
+ withCallState({
181
+ collection: 'flight'
182
+ }),
183
+ withEntities({
184
+ entity: type<Flight>(),
185
+ collection: 'flight'
186
+ }),
187
+ withDataService({
188
+ dataServiceType: FlightService,
189
+ filter: { from: 'Graz', to: 'Hamburg' },
190
+ collection: 'flight'
191
+ }),
192
+ withUndoRedo({
193
+ collections: ['flight'],
194
+ }),
195
+ );
196
+ ```
197
+
198
+ This setup makes them use ``flight`` as part of the used property names. As these implementations respect the Type Script type system, the compiler will make sure these properties are used in a typesafe way:
199
+
200
+ ```typescript
201
+ @Component(...)
202
+ export class FlightSearchDynamicComponent {
203
+ private store = inject(FlightBookingStore);
204
+
205
+ from = this.store.flightFilter.from;
206
+ to = this.store.flightFilter.to;
207
+ flights = this.store.flightEntities;
208
+ selected = this.store.selectedFlightEntities;
209
+ selectedIds = this.store.selectedFlightIds;
210
+
211
+ loading = this.store.flightLoading;
212
+
213
+ canUndo = this.store.canUndo;
214
+ canRedo = this.store.canRedo;
215
+
216
+ async search() {
217
+ this.store.loadFlightEntities();
218
+ }
219
+
220
+ undo(): void {
221
+ this.store.undo();
222
+ }
223
+
224
+ redo(): void {
225
+ this.store.redo();
226
+ }
227
+
228
+ updateCriteria(from: string, to: string): void {
229
+ this.store.updateFlightFilter({ from, to });
230
+ }
231
+
232
+ updateBasket(id: number, selected: boolean): void {
233
+ this.store.updateSelectedFlightEntities(id, selected);
234
+ }
235
+
236
+ }
237
+ ```
238
+
239
+ ## Redux Connector for the NgRx Signal Store `createReduxState()`
240
+
241
+ The Redux Connector turns any `signalStore()` into a Gobal State Management Slice following the Redux pattern.
242
+
243
+ It supports:
244
+
245
+ ✅ Well-known NgRx Store Actions \
246
+ ✅ Global Action `dispatch()` \
247
+ ✅ Angular Lazy Loading \
248
+ ✅ Auto-generated `provideNamedStore()` & `injectNamedStore()` Functions \
249
+ ✅ Global Action to Store Method Mappers \
250
+
251
+
252
+ ### Use a present Signal Store
253
+
254
+ ```typescript
255
+ export const FlightStore = signalStore(
256
+ // State
257
+ withEntities({ entity: type<Flight>(), collection: 'flight' }),
258
+ withEntities({ entity: type<number>(), collection: 'hide' }),
259
+ // Selectors
260
+ withComputed(({ flightEntities, hideEntities }) => ({
261
+ filteredFlights: computed(() => flightEntities()
262
+ .filter(flight => !hideEntities().includes(flight.id))),
263
+ flightCount: computed(() => flightEntities().length),
264
+ })),
265
+ // Updater
266
+ withMethods(store => ({
267
+ setFlights: (state: { flights: Flight[] }) => patchState(store,
268
+ setAllEntities(state.flights, { collection: 'flight' })),
269
+ updateFlight: (state: { flight: Flight }) => patchState(store,
270
+ updateEntity({ id: state.flight.id, changes: state.flight }, { collection: 'flight' })),
271
+ clearFlights: () => patchState(store,
272
+ removeAllEntities({ collection: 'flight' })),
273
+ })),
274
+ // Effects
275
+ withMethods((store, flightService = inject(FlightService)) => ({
276
+ loadFlights: reduxMethod<FlightFilter, { flights: Flight[] }>(pipe(
277
+ switchMap(filter => from(
278
+ flightService.load({ from: filter.from, to: filter.to })
279
+ )),
280
+ map(flights => ({ flights })),
281
+ ), store.setFlights),
282
+ })),
283
+ );
284
+ ```
285
+
286
+ ### Use well-known NgRx Store Actions
287
+
288
+ ```typescript
289
+ export const ticketActions = createActionGroup({
290
+ source: 'tickets',
291
+ events: {
292
+ 'flights load': props<FlightFilter>(),
293
+ 'flights loaded': props<{ flights: Flight[] }>(),
294
+ 'flights loaded by passenger': props<{ flights: Flight[] }>(),
295
+ 'flight update': props<{ flight: Flight }>(),
296
+ 'flights clear': emptyProps()
297
+ }
298
+ });
299
+ ```
300
+
301
+ ### Map Actions to Methods
302
+
303
+ ```typescript
304
+ export const { provideFlightStore, injectFlightStore } =
305
+ createReduxState('flight', FlightStore, store => withActionMappers(
306
+ mapAction(
307
+ // Filtered Action
308
+ ticketActions.flightsLoad,
309
+ // Side-Effect
310
+ store.loadFlights,
311
+ // Result Action
312
+ ticketActions.flightsLoaded),
313
+ mapAction(
314
+ // Filtered Actions
315
+ ticketActions.flightsLoaded, ticketActions.flightsLoadedByPassenger,
316
+ // State Updater Method (like Reducers)
317
+ store.setFlights
318
+ ),
319
+ mapAction(ticketActions.flightUpdate, store.updateFlight),
320
+ mapAction(ticketActions.flightsClear, store.clearFlights),
321
+ )
322
+ );
323
+ ```
324
+
325
+ ### Register an Angular Dependency Injection Provider
326
+
327
+ ```typescript
328
+ export const appRoutes: Route[] = [
329
+ {
330
+ path: 'flight-search-redux-connector',
331
+ providers: [provideFlightStore()],
332
+ component: FlightSearchReducConnectorComponent
333
+ },
334
+ ];
335
+ ```
336
+
337
+ ### Use the Store in your Component
338
+
339
+ ```typescript
340
+ @Component({
341
+ standalone: true,
342
+ imports: [
343
+ JsonPipe,
344
+ RouterLink,
345
+ FormsModule,
346
+ FlightCardComponent
347
+ ],
348
+ selector: 'demo-flight-search-redux-connector',
349
+ templateUrl: './flight-search.component.html',
350
+ })
351
+ export class FlightSearchReducConnectorComponent {
352
+ private store = injectFlightStore();
353
+
354
+ protected flights = this.store.flightEntities;
355
+
356
+ protected search() {
357
+ this.store.dispatch(
358
+ ticketActions.flightsLoad({
359
+ from: this.localState.filter.from(),
360
+ to: this.localState.filter.to()
361
+ })
362
+ );
363
+ }
364
+
365
+ protected reset(): void {
366
+ this.store.dispatch(ticketActions.flightsClear());
367
+ }
368
+ }
369
+ ```
package/esm2022/index.mjs CHANGED
@@ -3,4 +3,6 @@ export * from './lib/with-redux';
3
3
  export * from './lib/with-call-state';
4
4
  export * from './lib/with-undo-redo';
5
5
  export * from './lib/with-data-service';
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL25ncngtdG9vbGtpdC9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RSxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLHlCQUF5QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IHsgd2l0aERldnRvb2xzLCBwYXRjaFN0YXRlLCBBY3Rpb24gfSBmcm9tICcuL2xpYi93aXRoLWRldnRvb2xzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3dpdGgtcmVkdXgnO1xuZXhwb3J0ICogZnJvbSAnLi9saWIvd2l0aC1jYWxsLXN0YXRlJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3dpdGgtdW5kby1yZWRvJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL3dpdGgtZGF0YS1zZXJ2aWNlJztcbiJdfQ==
6
+ export * from './lib/redux-connector';
7
+ export * from './lib/redux-connector/rxjs-interop';
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWJzL25ncngtdG9vbGtpdC9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQVUsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RSxjQUFjLGtCQUFrQixDQUFDO0FBRWpDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLHlCQUF5QixDQUFDO0FBRXhDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxvQ0FBb0MsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IHdpdGhEZXZ0b29scywgcGF0Y2hTdGF0ZSwgQWN0aW9uIH0gZnJvbSAnLi9saWIvd2l0aC1kZXZ0b29scyc7XHJcbmV4cG9ydCAqIGZyb20gJy4vbGliL3dpdGgtcmVkdXgnO1xyXG5cclxuZXhwb3J0ICogZnJvbSAnLi9saWIvd2l0aC1jYWxsLXN0YXRlJztcclxuZXhwb3J0ICogZnJvbSAnLi9saWIvd2l0aC11bmRvLXJlZG8nO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi93aXRoLWRhdGEtc2VydmljZSc7XHJcblxyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9yZWR1eC1jb25uZWN0b3InO1xyXG5leHBvcnQgKiBmcm9tICcuL2xpYi9yZWR1eC1jb25uZWN0b3Ivcnhqcy1pbnRlcm9wJztcclxuIl19
@@ -3,4 +3,4 @@ export function assertActionFnSpecs(obj) {
3
3
  throw new Error('%o is not an Action Specification');
4
4
  }
5
5
  }
6
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXJ0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvbmdyeC10b29sa2l0L3NyYy9saWIvYXNzZXJ0aW9ucy9hc3NlcnRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsR0FBWTtJQUVaLElBQUksQ0FBQyxHQUFHLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFO1FBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLENBQUMsQ0FBQztLQUN0RDtBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBY3Rpb25zRm5TcGVjcyB9IGZyb20gJy4uL3dpdGgtcmVkdXgnO1xuXG5leHBvcnQgZnVuY3Rpb24gYXNzZXJ0QWN0aW9uRm5TcGVjcyhcbiAgb2JqOiB1bmtub3duXG4pOiBhc3NlcnRzIG9iaiBpcyBBY3Rpb25zRm5TcGVjcyB7XG4gIGlmICghb2JqIHx8IHR5cGVvZiBvYmogIT09ICdvYmplY3QnKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKCclbyBpcyBub3QgYW4gQWN0aW9uIFNwZWNpZmljYXRpb24nKTtcbiAgfVxufVxuIl19
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXNzZXJ0aW9ucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL2xpYnMvbmdyeC10b29sa2l0L3NyYy9saWIvYXNzZXJ0aW9ucy9hc3NlcnRpb25zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE1BQU0sVUFBVSxtQkFBbUIsQ0FDakMsR0FBWTtJQUVaLElBQUksQ0FBQyxHQUFHLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFFO1FBQ25DLE1BQU0sSUFBSSxLQUFLLENBQUMsbUNBQW1DLENBQUMsQ0FBQztLQUN0RDtBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBBY3Rpb25zRm5TcGVjcyB9IGZyb20gJy4uL3dpdGgtcmVkdXgnO1xyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIGFzc2VydEFjdGlvbkZuU3BlY3MoXHJcbiAgb2JqOiB1bmtub3duXHJcbik6IGFzc2VydHMgb2JqIGlzIEFjdGlvbnNGblNwZWNzIHtcclxuICBpZiAoIW9iaiB8fCB0eXBlb2Ygb2JqICE9PSAnb2JqZWN0Jykge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKCclbyBpcyBub3QgYW4gQWN0aW9uIFNwZWNpZmljYXRpb24nKTtcclxuICB9XHJcbn1cclxuIl19
@@ -0,0 +1,41 @@
1
+ import { ENVIRONMENT_INITIALIZER, inject, makeEnvironmentProviders } from "@angular/core";
2
+ import { SignalReduxStore, injectReduxDispatch } from "./signal-redux-store";
3
+ import { capitalize, isActionCreator } from "./util";
4
+ export function mapAction(...args) {
5
+ let resultMethod = args.pop();
6
+ let storeMethod = args.pop();
7
+ if (isActionCreator(storeMethod)) {
8
+ args.push(storeMethod);
9
+ storeMethod = resultMethod || storeMethod;
10
+ resultMethod = undefined;
11
+ }
12
+ const types = args.map((creator) => creator.type);
13
+ return {
14
+ types,
15
+ storeMethod,
16
+ resultMethod
17
+ };
18
+ }
19
+ export function withActionMappers(...mappers) {
20
+ return mappers;
21
+ }
22
+ export function createReduxState(storeName, signalStore, withActionMappers) {
23
+ const isRootProvider = signalStore?.ɵprov?.providedIn === 'root';
24
+ return {
25
+ [`provide${capitalize(storeName)}Store`]: (connectReduxDevtools = false) => makeEnvironmentProviders([
26
+ isRootProvider ? [] : signalStore,
27
+ {
28
+ provide: ENVIRONMENT_INITIALIZER,
29
+ multi: true,
30
+ useFactory: (signalReduxStore = inject(SignalReduxStore), store = inject(signalStore)) => () => {
31
+ if (connectReduxDevtools) {
32
+ // addStoreToReduxDevtools(store, storeName, false);
33
+ }
34
+ signalReduxStore.connectFeatureStore(withActionMappers(store));
35
+ }
36
+ }
37
+ ]),
38
+ [`inject${capitalize(storeName)}Store`]: () => Object.assign(inject(signalStore), { dispatch: injectReduxDispatch() })
39
+ };
40
+ }
41
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXJlZHV4LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9uZ3J4LXRvb2xraXQvc3JjL2xpYi9yZWR1eC1jb25uZWN0b3IvY3JlYXRlLXJlZHV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFHMUYsT0FBTyxFQUFFLGdCQUFnQixFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDN0UsT0FBTyxFQUFFLFVBQVUsRUFBRSxlQUFlLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFxQnJELE1BQU0sVUFBVSxTQUFTLENBR3ZCLEdBQUcsSUFJRjtJQUVELElBQUksWUFBWSxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQTJELENBQUM7SUFDdkYsSUFBSSxXQUFXLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBa0UsQ0FBQztJQUU3RixJQUFJLGVBQWUsQ0FBQyxXQUFXLENBQUMsRUFBRTtRQUNoQyxJQUFJLENBQUMsSUFBSSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3ZCLFdBQVcsR0FBRyxZQUFZLElBQUksV0FBVyxDQUFDO1FBQzFDLFlBQVksR0FBRyxTQUFTLENBQUM7S0FDMUI7SUFFRCxNQUFNLEtBQUssR0FBSSxJQUE0QixDQUFDLEdBQUcsQ0FDN0MsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQ2lCLENBQUM7SUFFN0MsT0FBTztRQUNMLEtBQUs7UUFDTCxXQUFXO1FBQ1gsWUFBWTtLQUNiLENBQUM7QUFDSixDQUFDO0FBRUQsTUFBTSxVQUFVLGlCQUFpQixDQUMvQixHQUFHLE9BQWlEO0lBRXBELE9BQU8sT0FBTyxDQUFDO0FBQ2pCLENBQUM7QUFFRCxNQUFNLFVBQVUsZ0JBQWdCLENBSTlCLFNBQW9CLEVBQ3BCLFdBQWtCLEVBQ2xCLGlCQUEyRjtJQUUzRixNQUFNLGNBQWMsR0FBSSxXQUFtQixFQUFFLEtBQUssRUFBRSxVQUFVLEtBQUssTUFBTSxDQUFDO0lBQzFFLE9BQU87UUFDTCxDQUFDLFVBQVUsVUFBVSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLG9CQUFvQixHQUFHLEtBQUssRUFBRSxFQUFFLENBQUMsd0JBQXdCLENBQUM7WUFDbkcsY0FBYyxDQUFBLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFdBQVc7WUFDaEM7Z0JBQ0UsT0FBTyxFQUFFLHVCQUF1QjtnQkFDaEMsS0FBSyxFQUFFLElBQUk7Z0JBQ1gsVUFBVSxFQUFFLENBQ1YsZ0JBQWdCLEdBQUcsTUFBTSxDQUFDLGdCQUFnQixDQUFDLEVBQzNDLEtBQUssR0FBRyxNQUFNLENBQUMsV0FBVyxDQUFDLEVBQzNCLEVBQUUsQ0FBQyxHQUFHLEVBQUU7b0JBQ1IsSUFBSSxvQkFBb0IsRUFBRTt3QkFDeEIsb0RBQW9EO3FCQUNyRDtvQkFDRCxnQkFBZ0IsQ0FBQyxtQkFBbUIsQ0FDbEMsaUJBQWlCLENBQUMsS0FBSyxDQUFDLENBQ3pCLENBQUM7Z0JBQ0osQ0FBQzthQUNGO1NBQ0YsQ0FBQztRQUNGLENBQUMsU0FBUyxVQUFVLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEdBQUcsRUFBRSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQzFELE1BQU0sQ0FBQyxXQUFXLENBQUMsRUFDbkIsRUFBRSxRQUFRLEVBQUUsbUJBQW1CLEVBQUUsRUFBRSxDQUNwQztLQUNvQyxDQUFDO0FBQzFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBFTlZJUk9OTUVOVF9JTklUSUFMSVpFUiwgaW5qZWN0LCBtYWtlRW52aXJvbm1lbnRQcm92aWRlcnMgfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xyXG5pbXBvcnQgeyBBY3Rpb25DcmVhdG9yLCBBY3Rpb25UeXBlIH0gZnJvbSBcIkBuZ3J4L3N0b3JlL3NyYy9tb2RlbHNcIjtcclxuaW1wb3J0IHsgQ3JlYXRlUmVkdXhTdGF0ZSwgRXh0cmFjdEFjdGlvblR5cGVzLCBNYXBwZXJUeXBlcywgU3RvcmUgfSBmcm9tIFwiLi9tb2RlbFwiO1xyXG5pbXBvcnQgeyBTaWduYWxSZWR1eFN0b3JlLCBpbmplY3RSZWR1eERpc3BhdGNoIH0gZnJvbSBcIi4vc2lnbmFsLXJlZHV4LXN0b3JlXCI7XHJcbmltcG9ydCB7IGNhcGl0YWxpemUsIGlzQWN0aW9uQ3JlYXRvciB9IGZyb20gXCIuL3V0aWxcIjtcclxuXHJcblxyXG5leHBvcnQgZnVuY3Rpb24gbWFwQWN0aW9uPFxyXG4gIENyZWF0b3JzIGV4dGVuZHMgcmVhZG9ubHkgQWN0aW9uQ3JlYXRvcltdXHJcbj4oXHJcbiAgLi4uYXJnczogW1xyXG4gICAgLi4uY3JlYXRvcnM6IENyZWF0b3JzLFxyXG4gICAgc3RvcmVNZXRob2Q6IChhY3Rpb246IEFjdGlvblR5cGU8Q3JlYXRvcnNbbnVtYmVyXT4pID0+IHVua25vd25cclxuICBdXHJcbik6IE1hcHBlclR5cGVzPENyZWF0b3JzPjtcclxuZXhwb3J0IGZ1bmN0aW9uIG1hcEFjdGlvbjxcclxuICBDcmVhdG9ycyBleHRlbmRzIHJlYWRvbmx5IEFjdGlvbkNyZWF0b3JbXSxcclxuICBUXHJcbj4oXHJcbiAgLi4uYXJnczogW1xyXG4gICAgLi4uY3JlYXRvcnM6IENyZWF0b3JzLFxyXG4gICAgc3RvcmVNZXRob2Q6IChhY3Rpb246IEFjdGlvblR5cGU8Q3JlYXRvcnNbbnVtYmVyXT4sIHJlc3VsdE1ldGhvZDogKGlucHV0OiBUKSA9PiB1bmtub3duKSA9PiB1bmtub3duLFxyXG4gICAgcmVzdWx0TWV0aG9kOiAoaW5wdXQ6IFQpID0+IHVua25vd25cclxuICBdXHJcbik6IE1hcHBlclR5cGVzPENyZWF0b3JzPjtcclxuZXhwb3J0IGZ1bmN0aW9uIG1hcEFjdGlvbjxcclxuICBDcmVhdG9ycyBleHRlbmRzIHJlYWRvbmx5IEFjdGlvbkNyZWF0b3JbXVxyXG4+KFxyXG4gIC4uLmFyZ3M6IFtcclxuICAgIC4uLmNyZWF0b3JzOiBDcmVhdG9ycyxcclxuICAgIHN0b3JlTWV0aG9kOiAoYWN0aW9uOiBBY3Rpb25UeXBlPENyZWF0b3JzW251bWJlcl0+KSA9PiB1bmtub3duLFxyXG4gICAgcmVzdWx0TWV0aG9kPzogKGlucHV0OiB1bmtub3duKSA9PiB1bmtub3duXHJcbiAgXVxyXG4pOiBNYXBwZXJUeXBlczxDcmVhdG9ycz4ge1xyXG4gIGxldCByZXN1bHRNZXRob2QgPSBhcmdzLnBvcCgpIGFzIHVua25vd24gYXMgKChpbnB1dDogdW5rbm93bikgPT4gdW5rbm93biApIHwgdW5kZWZpbmVkO1xyXG4gIGxldCBzdG9yZU1ldGhvZCA9IGFyZ3MucG9wKCkgYXMgdW5rbm93biBhcyAoYWN0aW9uOiBBY3Rpb25UeXBlPENyZWF0b3JzW251bWJlcl0+KSA9PiB1bmtub3duO1xyXG5cclxuICBpZiAoaXNBY3Rpb25DcmVhdG9yKHN0b3JlTWV0aG9kKSkge1xyXG4gICAgYXJncy5wdXNoKHN0b3JlTWV0aG9kKTtcclxuICAgIHN0b3JlTWV0aG9kID0gcmVzdWx0TWV0aG9kIHx8IHN0b3JlTWV0aG9kO1xyXG4gICAgcmVzdWx0TWV0aG9kID0gdW5kZWZpbmVkO1xyXG4gIH1cclxuXHJcbiAgY29uc3QgdHlwZXMgPSAoYXJncyBhcyB1bmtub3duIGFzIENyZWF0b3JzKS5tYXAoXHJcbiAgICAoY3JlYXRvcikgPT4gY3JlYXRvci50eXBlXHJcbiAgKSBhcyB1bmtub3duIGFzIEV4dHJhY3RBY3Rpb25UeXBlczxDcmVhdG9ycz47XHJcblxyXG4gIHJldHVybiB7XHJcbiAgICB0eXBlcyxcclxuICAgIHN0b3JlTWV0aG9kLFxyXG4gICAgcmVzdWx0TWV0aG9kXHJcbiAgfTtcclxufVxyXG5cclxuZXhwb3J0IGZ1bmN0aW9uIHdpdGhBY3Rpb25NYXBwZXJzKFxyXG4gIC4uLm1hcHBlcnM6IE1hcHBlclR5cGVzPEFjdGlvbkNyZWF0b3I8YW55LCBhbnk+W10+W11cclxuKTogTWFwcGVyVHlwZXM8QWN0aW9uQ3JlYXRvcjxhbnksIGFueT5bXT5bXSB7XHJcbiAgcmV0dXJuIG1hcHBlcnM7XHJcbn1cclxuXHJcbmV4cG9ydCBmdW5jdGlvbiBjcmVhdGVSZWR1eFN0YXRlPFxyXG4gIFN0b3JlTmFtZSBleHRlbmRzIHN0cmluZyxcclxuICBTVE9SRSBleHRlbmRzIFN0b3JlXHJcbj4oXHJcbiAgc3RvcmVOYW1lOiBTdG9yZU5hbWUsXHJcbiAgc2lnbmFsU3RvcmU6IFNUT1JFLFxyXG4gIHdpdGhBY3Rpb25NYXBwZXJzOiAoc3RvcmU6IEluc3RhbmNlVHlwZTxTVE9SRT4pID0+IE1hcHBlclR5cGVzPEFjdGlvbkNyZWF0b3I8YW55LCBhbnk+W10+W10sXHJcbik6IENyZWF0ZVJlZHV4U3RhdGU8U3RvcmVOYW1lLCBTVE9SRT4ge1xyXG4gIGNvbnN0IGlzUm9vdFByb3ZpZGVyID0gKHNpZ25hbFN0b3JlIGFzIGFueSk/Lsm1cHJvdj8ucHJvdmlkZWRJbiA9PT0gJ3Jvb3QnO1xyXG4gIHJldHVybiB7XHJcbiAgICBbYHByb3ZpZGUke2NhcGl0YWxpemUoc3RvcmVOYW1lKX1TdG9yZWBdOiAoY29ubmVjdFJlZHV4RGV2dG9vbHMgPSBmYWxzZSkgPT4gbWFrZUVudmlyb25tZW50UHJvdmlkZXJzKFtcclxuICAgICAgaXNSb290UHJvdmlkZXI/IFtdIDogc2lnbmFsU3RvcmUsXHJcbiAgICAgIHtcclxuICAgICAgICBwcm92aWRlOiBFTlZJUk9OTUVOVF9JTklUSUFMSVpFUixcclxuICAgICAgICBtdWx0aTogdHJ1ZSxcclxuICAgICAgICB1c2VGYWN0b3J5OiAoXHJcbiAgICAgICAgICBzaWduYWxSZWR1eFN0b3JlID0gaW5qZWN0KFNpZ25hbFJlZHV4U3RvcmUpLFxyXG4gICAgICAgICAgc3RvcmUgPSBpbmplY3Qoc2lnbmFsU3RvcmUpXHJcbiAgICAgICAgKSA9PiAoKSA9PiB7XHJcbiAgICAgICAgICBpZiAoY29ubmVjdFJlZHV4RGV2dG9vbHMpIHtcclxuICAgICAgICAgICAgLy8gYWRkU3RvcmVUb1JlZHV4RGV2dG9vbHMoc3RvcmUsIHN0b3JlTmFtZSwgZmFsc2UpO1xyXG4gICAgICAgICAgfVxyXG4gICAgICAgICAgc2lnbmFsUmVkdXhTdG9yZS5jb25uZWN0RmVhdHVyZVN0b3JlKFxyXG4gICAgICAgICAgICB3aXRoQWN0aW9uTWFwcGVycyhzdG9yZSlcclxuICAgICAgICAgICk7XHJcbiAgICAgICAgfVxyXG4gICAgICB9XHJcbiAgICBdKSxcclxuICAgIFtgaW5qZWN0JHtjYXBpdGFsaXplKHN0b3JlTmFtZSl9U3RvcmVgXTogKCkgPT4gT2JqZWN0LmFzc2lnbihcclxuICAgICAgaW5qZWN0KHNpZ25hbFN0b3JlKSxcclxuICAgICAgeyBkaXNwYXRjaDogaW5qZWN0UmVkdXhEaXNwYXRjaCgpIH1cclxuICAgIClcclxuICB9IGFzIENyZWF0ZVJlZHV4U3RhdGU8U3RvcmVOYW1lLCBTVE9SRT47XHJcbn1cclxuIl19