@ngrx/store-devtools 14.0.2 → 14.3.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.
@@ -1,70 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, Injectable, Inject, NgModule } from '@angular/core';
3
3
  import * as i2 from '@ngrx/store';
4
- import { INIT, UPDATE, ActionsSubject, INITIAL_STATE, StateObservable, ReducerManagerDispatcher } from '@ngrx/store';
4
+ import { ActionsSubject, UPDATE, INIT, INITIAL_STATE, StateObservable, ReducerManagerDispatcher } from '@ngrx/store';
5
5
  import { EMPTY, Observable, of, merge, queueScheduler, ReplaySubject } from 'rxjs';
6
6
  import { share, filter, map, concatMap, timeout, debounceTime, catchError, take, takeUntil, switchMap, skip, observeOn, withLatestFrom, scan } from 'rxjs/operators';
7
7
 
8
- /**
9
- * Chrome extension documentation
10
- * @see https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md
11
- * Firefox extension documentation
12
- * @see https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md
13
- */
14
- class StoreDevtoolsConfig {
15
- constructor() {
16
- /**
17
- * Maximum allowed actions to be stored in the history tree (default: `false`)
18
- */
19
- this.maxAge = false;
20
- }
21
- }
22
- const STORE_DEVTOOLS_CONFIG = new InjectionToken('@ngrx/store-devtools Options');
23
- /**
24
- * Used to provide a `StoreDevtoolsConfig` for the store-devtools.
25
- */
26
- const INITIAL_OPTIONS = new InjectionToken('@ngrx/store-devtools Initial Config');
27
- function noMonitor() {
28
- return null;
29
- }
30
- const DEFAULT_NAME = 'NgRx Store DevTools';
31
- function createConfig(optionsInput) {
32
- const DEFAULT_OPTIONS = {
33
- maxAge: false,
34
- monitor: noMonitor,
35
- actionSanitizer: undefined,
36
- stateSanitizer: undefined,
37
- name: DEFAULT_NAME,
38
- serialize: false,
39
- logOnly: false,
40
- autoPause: false,
41
- // Add all features explicitly. This prevent buggy behavior for
42
- // options like "lock" which might otherwise not show up.
43
- features: {
44
- pause: true,
45
- lock: true,
46
- persist: true,
47
- export: true,
48
- import: 'custom',
49
- jump: true,
50
- skip: true,
51
- reorder: true,
52
- dispatch: true,
53
- test: true, // Generate tests for the selected actions
54
- },
55
- };
56
- const options = typeof optionsInput === 'function' ? optionsInput() : optionsInput;
57
- const logOnly = options.logOnly
58
- ? { pause: true, export: true, test: true }
59
- : false;
60
- const features = options.features || logOnly || DEFAULT_OPTIONS.features;
61
- const config = Object.assign({}, DEFAULT_OPTIONS, { features }, options);
62
- if (config.maxAge && config.maxAge < 2) {
63
- throw new Error(`Devtools 'maxAge' cannot be less than 2, got ${config.maxAge}`);
64
- }
65
- return config;
66
- }
67
-
68
8
  const PERFORM_ACTION = 'PERFORM_ACTION';
69
9
  const REFRESH = 'REFRESH';
70
10
  const RESET = 'RESET';
@@ -162,6 +102,66 @@ class PauseRecording {
162
102
  }
163
103
  }
164
104
 
105
+ /**
106
+ * Chrome extension documentation
107
+ * @see https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md
108
+ * Firefox extension documentation
109
+ * @see https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md
110
+ */
111
+ class StoreDevtoolsConfig {
112
+ constructor() {
113
+ /**
114
+ * Maximum allowed actions to be stored in the history tree (default: `false`)
115
+ */
116
+ this.maxAge = false;
117
+ }
118
+ }
119
+ const STORE_DEVTOOLS_CONFIG = new InjectionToken('@ngrx/store-devtools Options');
120
+ /**
121
+ * Used to provide a `StoreDevtoolsConfig` for the store-devtools.
122
+ */
123
+ const INITIAL_OPTIONS = new InjectionToken('@ngrx/store-devtools Initial Config');
124
+ function noMonitor() {
125
+ return null;
126
+ }
127
+ const DEFAULT_NAME = 'NgRx Store DevTools';
128
+ function createConfig(optionsInput) {
129
+ const DEFAULT_OPTIONS = {
130
+ maxAge: false,
131
+ monitor: noMonitor,
132
+ actionSanitizer: undefined,
133
+ stateSanitizer: undefined,
134
+ name: DEFAULT_NAME,
135
+ serialize: false,
136
+ logOnly: false,
137
+ autoPause: false,
138
+ // Add all features explicitly. This prevent buggy behavior for
139
+ // options like "lock" which might otherwise not show up.
140
+ features: {
141
+ pause: true,
142
+ lock: true,
143
+ persist: true,
144
+ export: true,
145
+ import: 'custom',
146
+ jump: true,
147
+ skip: true,
148
+ reorder: true,
149
+ dispatch: true,
150
+ test: true, // Generate tests for the selected actions
151
+ },
152
+ };
153
+ const options = typeof optionsInput === 'function' ? optionsInput() : optionsInput;
154
+ const logOnly = options.logOnly
155
+ ? { pause: true, export: true, test: true }
156
+ : false;
157
+ const features = options.features || logOnly || DEFAULT_OPTIONS.features;
158
+ const config = Object.assign({}, DEFAULT_OPTIONS, { features }, options);
159
+ if (config.maxAge && config.maxAge < 2) {
160
+ throw new Error(`Devtools 'maxAge' cannot be less than 2, got ${config.maxAge}`);
161
+ }
162
+ return config;
163
+ }
164
+
165
165
  function difference(first, second) {
166
166
  return first.filter((item) => second.indexOf(item) < 0);
167
167
  }
@@ -275,6 +275,165 @@ function escapeRegExp(s) {
275
275
  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
276
276
  }
277
277
 
278
+ class DevtoolsDispatcher extends ActionsSubject {
279
+ }
280
+ /** @nocollapse */ DevtoolsDispatcher.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
281
+ /** @nocollapse */ DevtoolsDispatcher.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher });
282
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher, decorators: [{
283
+ type: Injectable
284
+ }] });
285
+
286
+ const ExtensionActionTypes = {
287
+ START: 'START',
288
+ DISPATCH: 'DISPATCH',
289
+ STOP: 'STOP',
290
+ ACTION: 'ACTION',
291
+ };
292
+ const REDUX_DEVTOOLS_EXTENSION = new InjectionToken('@ngrx/store-devtools Redux Devtools Extension');
293
+ class DevtoolsExtension {
294
+ constructor(devtoolsExtension, config, dispatcher) {
295
+ this.config = config;
296
+ this.dispatcher = dispatcher;
297
+ this.devtoolsExtension = devtoolsExtension;
298
+ this.createActionStreams();
299
+ }
300
+ notify(action, state) {
301
+ if (!this.devtoolsExtension) {
302
+ return;
303
+ }
304
+ // Check to see if the action requires a full update of the liftedState.
305
+ // If it is a simple action generated by the user's app and the recording
306
+ // is not locked/paused, only send the action and the current state (fast).
307
+ //
308
+ // A full liftedState update (slow: serializes the entire liftedState) is
309
+ // only required when:
310
+ // a) redux-devtools-extension fires the @@Init action (ignored by
311
+ // @ngrx/store-devtools)
312
+ // b) an action is generated by an @ngrx module (e.g. @ngrx/effects/init
313
+ // or @ngrx/store/update-reducers)
314
+ // c) the state has been recomputed due to time-traveling
315
+ // d) any action that is not a PerformAction to err on the side of
316
+ // caution.
317
+ if (action.type === PERFORM_ACTION) {
318
+ if (state.isLocked || state.isPaused) {
319
+ return;
320
+ }
321
+ const currentState = unliftState(state);
322
+ if (shouldFilterActions(this.config) &&
323
+ isActionFiltered(currentState, action, this.config.predicate, this.config.actionsSafelist, this.config.actionsBlocklist)) {
324
+ return;
325
+ }
326
+ const sanitizedState = this.config.stateSanitizer
327
+ ? sanitizeState(this.config.stateSanitizer, currentState, state.currentStateIndex)
328
+ : currentState;
329
+ const sanitizedAction = this.config.actionSanitizer
330
+ ? sanitizeAction(this.config.actionSanitizer, action, state.nextActionId)
331
+ : action;
332
+ this.sendToReduxDevtools(() => this.extensionConnection.send(sanitizedAction, sanitizedState));
333
+ }
334
+ else {
335
+ // Requires full state update
336
+ const sanitizedLiftedState = {
337
+ ...state,
338
+ stagedActionIds: state.stagedActionIds,
339
+ actionsById: this.config.actionSanitizer
340
+ ? sanitizeActions(this.config.actionSanitizer, state.actionsById)
341
+ : state.actionsById,
342
+ computedStates: this.config.stateSanitizer
343
+ ? sanitizeStates(this.config.stateSanitizer, state.computedStates)
344
+ : state.computedStates,
345
+ };
346
+ this.sendToReduxDevtools(() => this.devtoolsExtension.send(null, sanitizedLiftedState, this.getExtensionConfig(this.config)));
347
+ }
348
+ }
349
+ createChangesObservable() {
350
+ if (!this.devtoolsExtension) {
351
+ return EMPTY;
352
+ }
353
+ return new Observable((subscriber) => {
354
+ const connection = this.devtoolsExtension.connect(this.getExtensionConfig(this.config));
355
+ this.extensionConnection = connection;
356
+ connection.init();
357
+ connection.subscribe((change) => subscriber.next(change));
358
+ return connection.unsubscribe;
359
+ });
360
+ }
361
+ createActionStreams() {
362
+ // Listens to all changes
363
+ const changes$ = this.createChangesObservable().pipe(share());
364
+ // Listen for the start action
365
+ const start$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.START));
366
+ // Listen for the stop action
367
+ const stop$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.STOP));
368
+ // Listen for lifted actions
369
+ const liftedActions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.DISPATCH), map((change) => this.unwrapAction(change.payload)), concatMap((action) => {
370
+ if (action.type === IMPORT_STATE) {
371
+ // State imports may happen in two situations:
372
+ // 1. Explicitly by user
373
+ // 2. User activated the "persist state accross reloads" option
374
+ // and now the state is imported during reload.
375
+ // Because of option 2, we need to give possible
376
+ // lazy loaded reducers time to instantiate.
377
+ // As soon as there is no UPDATE action within 1 second,
378
+ // it is assumed that all reducers are loaded.
379
+ return this.dispatcher.pipe(filter((action) => action.type === UPDATE), timeout(1000), debounceTime(1000), map(() => action), catchError(() => of(action)), take(1));
380
+ }
381
+ else {
382
+ return of(action);
383
+ }
384
+ }));
385
+ // Listen for unlifted actions
386
+ const actions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.ACTION), map((change) => this.unwrapAction(change.payload)));
387
+ const actionsUntilStop$ = actions$.pipe(takeUntil(stop$));
388
+ const liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$));
389
+ this.start$ = start$.pipe(takeUntil(stop$));
390
+ // Only take the action sources between the start/stop events
391
+ this.actions$ = this.start$.pipe(switchMap(() => actionsUntilStop$));
392
+ this.liftedActions$ = this.start$.pipe(switchMap(() => liftedUntilStop$));
393
+ }
394
+ unwrapAction(action) {
395
+ return typeof action === 'string' ? eval(`(${action})`) : action;
396
+ }
397
+ getExtensionConfig(config) {
398
+ const extensionOptions = {
399
+ name: config.name,
400
+ features: config.features,
401
+ serialize: config.serialize,
402
+ autoPause: config.autoPause ?? false,
403
+ // The action/state sanitizers are not added to the config
404
+ // because sanitation is done in this class already.
405
+ // It is done before sending it to the devtools extension for consistency:
406
+ // - If we call extensionConnection.send(...),
407
+ // the extension would call the sanitizers.
408
+ // - If we call devtoolsExtension.send(...) (aka full state update),
409
+ // the extension would NOT call the sanitizers, so we have to do it ourselves.
410
+ };
411
+ if (config.maxAge !== false /* support === 0 */) {
412
+ extensionOptions.maxAge = config.maxAge;
413
+ }
414
+ return extensionOptions;
415
+ }
416
+ sendToReduxDevtools(send) {
417
+ try {
418
+ send();
419
+ }
420
+ catch (err) {
421
+ console.warn('@ngrx/store-devtools: something went wrong inside the redux devtools', err);
422
+ }
423
+ }
424
+ }
425
+ /** @nocollapse */ DevtoolsExtension.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension, deps: [{ token: REDUX_DEVTOOLS_EXTENSION }, { token: STORE_DEVTOOLS_CONFIG }, { token: DevtoolsDispatcher }], target: i0.ɵɵFactoryTarget.Injectable });
426
+ /** @nocollapse */ DevtoolsExtension.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension });
427
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension, decorators: [{
428
+ type: Injectable
429
+ }], ctorParameters: function () { return [{ type: undefined, decorators: [{
430
+ type: Inject,
431
+ args: [REDUX_DEVTOOLS_EXTENSION]
432
+ }] }, { type: StoreDevtoolsConfig, decorators: [{
433
+ type: Inject,
434
+ args: [STORE_DEVTOOLS_CONFIG]
435
+ }] }, { type: DevtoolsDispatcher }]; } });
436
+
278
437
  const INIT_ACTION = { type: INIT };
279
438
  const RECOMPUTE = '@ngrx/store-devtools/recompute';
280
439
  const RECOMPUTE_ACTION = { type: RECOMPUTE };
@@ -639,165 +798,6 @@ function liftReducerWith(initialCommittedState, initialLiftedState, errorHandler
639
798
  };
640
799
  }
641
800
 
642
- class DevtoolsDispatcher extends ActionsSubject {
643
- }
644
- /** @nocollapse */ DevtoolsDispatcher.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
645
- /** @nocollapse */ DevtoolsDispatcher.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher });
646
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsDispatcher, decorators: [{
647
- type: Injectable
648
- }] });
649
-
650
- const ExtensionActionTypes = {
651
- START: 'START',
652
- DISPATCH: 'DISPATCH',
653
- STOP: 'STOP',
654
- ACTION: 'ACTION',
655
- };
656
- const REDUX_DEVTOOLS_EXTENSION = new InjectionToken('@ngrx/store-devtools Redux Devtools Extension');
657
- class DevtoolsExtension {
658
- constructor(devtoolsExtension, config, dispatcher) {
659
- this.config = config;
660
- this.dispatcher = dispatcher;
661
- this.devtoolsExtension = devtoolsExtension;
662
- this.createActionStreams();
663
- }
664
- notify(action, state) {
665
- if (!this.devtoolsExtension) {
666
- return;
667
- }
668
- // Check to see if the action requires a full update of the liftedState.
669
- // If it is a simple action generated by the user's app and the recording
670
- // is not locked/paused, only send the action and the current state (fast).
671
- //
672
- // A full liftedState update (slow: serializes the entire liftedState) is
673
- // only required when:
674
- // a) redux-devtools-extension fires the @@Init action (ignored by
675
- // @ngrx/store-devtools)
676
- // b) an action is generated by an @ngrx module (e.g. @ngrx/effects/init
677
- // or @ngrx/store/update-reducers)
678
- // c) the state has been recomputed due to time-traveling
679
- // d) any action that is not a PerformAction to err on the side of
680
- // caution.
681
- if (action.type === PERFORM_ACTION) {
682
- if (state.isLocked || state.isPaused) {
683
- return;
684
- }
685
- const currentState = unliftState(state);
686
- if (shouldFilterActions(this.config) &&
687
- isActionFiltered(currentState, action, this.config.predicate, this.config.actionsSafelist, this.config.actionsBlocklist)) {
688
- return;
689
- }
690
- const sanitizedState = this.config.stateSanitizer
691
- ? sanitizeState(this.config.stateSanitizer, currentState, state.currentStateIndex)
692
- : currentState;
693
- const sanitizedAction = this.config.actionSanitizer
694
- ? sanitizeAction(this.config.actionSanitizer, action, state.nextActionId)
695
- : action;
696
- this.sendToReduxDevtools(() => this.extensionConnection.send(sanitizedAction, sanitizedState));
697
- }
698
- else {
699
- // Requires full state update
700
- const sanitizedLiftedState = {
701
- ...state,
702
- stagedActionIds: state.stagedActionIds,
703
- actionsById: this.config.actionSanitizer
704
- ? sanitizeActions(this.config.actionSanitizer, state.actionsById)
705
- : state.actionsById,
706
- computedStates: this.config.stateSanitizer
707
- ? sanitizeStates(this.config.stateSanitizer, state.computedStates)
708
- : state.computedStates,
709
- };
710
- this.sendToReduxDevtools(() => this.devtoolsExtension.send(null, sanitizedLiftedState, this.getExtensionConfig(this.config)));
711
- }
712
- }
713
- createChangesObservable() {
714
- if (!this.devtoolsExtension) {
715
- return EMPTY;
716
- }
717
- return new Observable((subscriber) => {
718
- const connection = this.devtoolsExtension.connect(this.getExtensionConfig(this.config));
719
- this.extensionConnection = connection;
720
- connection.init();
721
- connection.subscribe((change) => subscriber.next(change));
722
- return connection.unsubscribe;
723
- });
724
- }
725
- createActionStreams() {
726
- // Listens to all changes
727
- const changes$ = this.createChangesObservable().pipe(share());
728
- // Listen for the start action
729
- const start$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.START));
730
- // Listen for the stop action
731
- const stop$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.STOP));
732
- // Listen for lifted actions
733
- const liftedActions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.DISPATCH), map((change) => this.unwrapAction(change.payload)), concatMap((action) => {
734
- if (action.type === IMPORT_STATE) {
735
- // State imports may happen in two situations:
736
- // 1. Explicitly by user
737
- // 2. User activated the "persist state accross reloads" option
738
- // and now the state is imported during reload.
739
- // Because of option 2, we need to give possible
740
- // lazy loaded reducers time to instantiate.
741
- // As soon as there is no UPDATE action within 1 second,
742
- // it is assumed that all reducers are loaded.
743
- return this.dispatcher.pipe(filter((action) => action.type === UPDATE), timeout(1000), debounceTime(1000), map(() => action), catchError(() => of(action)), take(1));
744
- }
745
- else {
746
- return of(action);
747
- }
748
- }));
749
- // Listen for unlifted actions
750
- const actions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.ACTION), map((change) => this.unwrapAction(change.payload)));
751
- const actionsUntilStop$ = actions$.pipe(takeUntil(stop$));
752
- const liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$));
753
- this.start$ = start$.pipe(takeUntil(stop$));
754
- // Only take the action sources between the start/stop events
755
- this.actions$ = this.start$.pipe(switchMap(() => actionsUntilStop$));
756
- this.liftedActions$ = this.start$.pipe(switchMap(() => liftedUntilStop$));
757
- }
758
- unwrapAction(action) {
759
- return typeof action === 'string' ? eval(`(${action})`) : action;
760
- }
761
- getExtensionConfig(config) {
762
- const extensionOptions = {
763
- name: config.name,
764
- features: config.features,
765
- serialize: config.serialize,
766
- autoPause: config.autoPause ?? false,
767
- // The action/state sanitizers are not added to the config
768
- // because sanitation is done in this class already.
769
- // It is done before sending it to the devtools extension for consistency:
770
- // - If we call extensionConnection.send(...),
771
- // the extension would call the sanitizers.
772
- // - If we call devtoolsExtension.send(...) (aka full state update),
773
- // the extension would NOT call the sanitizers, so we have to do it ourselves.
774
- };
775
- if (config.maxAge !== false /* support === 0 */) {
776
- extensionOptions.maxAge = config.maxAge;
777
- }
778
- return extensionOptions;
779
- }
780
- sendToReduxDevtools(send) {
781
- try {
782
- send();
783
- }
784
- catch (err) {
785
- console.warn('@ngrx/store-devtools: something went wrong inside the redux devtools', err);
786
- }
787
- }
788
- }
789
- /** @nocollapse */ DevtoolsExtension.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension, deps: [{ token: REDUX_DEVTOOLS_EXTENSION }, { token: STORE_DEVTOOLS_CONFIG }, { token: DevtoolsDispatcher }], target: i0.ɵɵFactoryTarget.Injectable });
790
- /** @nocollapse */ DevtoolsExtension.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension });
791
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.1", ngImport: i0, type: DevtoolsExtension, decorators: [{
792
- type: Injectable
793
- }], ctorParameters: function () { return [{ type: undefined, decorators: [{
794
- type: Inject,
795
- args: [REDUX_DEVTOOLS_EXTENSION]
796
- }] }, { type: StoreDevtoolsConfig, decorators: [{
797
- type: Inject,
798
- args: [STORE_DEVTOOLS_CONFIG]
799
- }] }, { type: DevtoolsDispatcher }]; } });
800
-
801
801
  class StoreDevtools {
802
802
  constructor(dispatcher, actions$, reducers$, extension, scannedActions, errorHandler, initialState, config) {
803
803
  const liftedInitialState = liftInitialState(initialState, config.monitor);
@@ -906,6 +906,43 @@ function createReduxDevtoolsExtension() {
906
906
  return null;
907
907
  }
908
908
  }
909
+ function provideStoreDevtools(options = {}) {
910
+ return {
911
+ ɵproviders: [
912
+ DevtoolsExtension,
913
+ DevtoolsDispatcher,
914
+ StoreDevtools,
915
+ {
916
+ provide: INITIAL_OPTIONS,
917
+ useValue: options,
918
+ },
919
+ {
920
+ provide: IS_EXTENSION_OR_MONITOR_PRESENT,
921
+ deps: [REDUX_DEVTOOLS_EXTENSION, STORE_DEVTOOLS_CONFIG],
922
+ useFactory: createIsExtensionOrMonitorPresent,
923
+ },
924
+ {
925
+ provide: REDUX_DEVTOOLS_EXTENSION,
926
+ useFactory: createReduxDevtoolsExtension,
927
+ },
928
+ {
929
+ provide: STORE_DEVTOOLS_CONFIG,
930
+ deps: [INITIAL_OPTIONS],
931
+ useFactory: createConfig,
932
+ },
933
+ {
934
+ provide: StateObservable,
935
+ deps: [StoreDevtools],
936
+ useFactory: createStateObservable,
937
+ },
938
+ {
939
+ provide: ReducerManagerDispatcher,
940
+ useExisting: DevtoolsDispatcher,
941
+ },
942
+ ],
943
+ };
944
+ }
945
+
909
946
  function createStateObservable(devtools) {
910
947
  return devtools.state;
911
948
  }
@@ -913,38 +950,7 @@ class StoreDevtoolsModule {
913
950
  static instrument(options = {}) {
914
951
  return {
915
952
  ngModule: StoreDevtoolsModule,
916
- providers: [
917
- DevtoolsExtension,
918
- DevtoolsDispatcher,
919
- StoreDevtools,
920
- {
921
- provide: INITIAL_OPTIONS,
922
- useValue: options,
923
- },
924
- {
925
- provide: IS_EXTENSION_OR_MONITOR_PRESENT,
926
- deps: [REDUX_DEVTOOLS_EXTENSION, STORE_DEVTOOLS_CONFIG],
927
- useFactory: createIsExtensionOrMonitorPresent,
928
- },
929
- {
930
- provide: REDUX_DEVTOOLS_EXTENSION,
931
- useFactory: createReduxDevtoolsExtension,
932
- },
933
- {
934
- provide: STORE_DEVTOOLS_CONFIG,
935
- deps: [INITIAL_OPTIONS],
936
- useFactory: createConfig,
937
- },
938
- {
939
- provide: StateObservable,
940
- deps: [StoreDevtools],
941
- useFactory: createStateObservable,
942
- },
943
- {
944
- provide: ReducerManagerDispatcher,
945
- useExisting: DevtoolsDispatcher,
946
- },
947
- ],
953
+ providers: [...provideStoreDevtools(options).ɵproviders],
948
954
  };
949
955
  }
950
956
  }
@@ -966,5 +972,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.0-rc.1", ng
966
972
  * Generated bundle index. Do not edit.
967
973
  */
968
974
 
969
- export { INITIAL_OPTIONS, RECOMPUTE, REDUX_DEVTOOLS_EXTENSION, StoreDevtools, StoreDevtoolsConfig, StoreDevtoolsModule };
975
+ export { INITIAL_OPTIONS, RECOMPUTE, REDUX_DEVTOOLS_EXTENSION, StoreDevtools, StoreDevtoolsConfig, StoreDevtoolsModule, provideStoreDevtools };
970
976
  //# sourceMappingURL=ngrx-store-devtools.mjs.map