@ngrx/store-devtools 18.0.0-beta.1 → 18.0.0-rc.1

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,890 +1,1047 @@
1
- // src/actions.mjs
2
- var PERFORM_ACTION = "PERFORM_ACTION";
3
- var REFRESH = "REFRESH";
4
- var RESET = "RESET";
5
- var ROLLBACK = "ROLLBACK";
6
- var COMMIT = "COMMIT";
7
- var SWEEP = "SWEEP";
8
- var TOGGLE_ACTION = "TOGGLE_ACTION";
9
- var SET_ACTIONS_ACTIVE = "SET_ACTIONS_ACTIVE";
10
- var JUMP_TO_STATE = "JUMP_TO_STATE";
11
- var JUMP_TO_ACTION = "JUMP_TO_ACTION";
12
- var IMPORT_STATE = "IMPORT_STATE";
13
- var LOCK_CHANGES = "LOCK_CHANGES";
14
- var PAUSE_RECORDING = "PAUSE_RECORDING";
15
- var PerformAction = class {
16
- constructor(action, timestamp) {
17
- this.action = action;
18
- this.timestamp = timestamp;
19
- this.type = PERFORM_ACTION;
20
- if (typeof action.type === "undefined") {
21
- throw new Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');
22
- }
23
- }
24
- };
25
- var Refresh = class {
26
- constructor() {
27
- this.type = REFRESH;
28
- }
29
- };
30
- var Reset = class {
31
- constructor(timestamp) {
32
- this.timestamp = timestamp;
33
- this.type = RESET;
34
- }
35
- };
36
- var Rollback = class {
37
- constructor(timestamp) {
38
- this.timestamp = timestamp;
39
- this.type = ROLLBACK;
40
- }
41
- };
42
- var Commit = class {
43
- constructor(timestamp) {
44
- this.timestamp = timestamp;
45
- this.type = COMMIT;
46
- }
47
- };
48
- var Sweep = class {
49
- constructor() {
50
- this.type = SWEEP;
51
- }
52
- };
53
- var ToggleAction = class {
54
- constructor(id) {
55
- this.id = id;
56
- this.type = TOGGLE_ACTION;
57
- }
58
- };
59
- var SetActionsActive = class {
60
- constructor(start, end, active = true) {
61
- this.start = start;
62
- this.end = end;
63
- this.active = active;
64
- this.type = SET_ACTIONS_ACTIVE;
65
- }
66
- };
67
- var JumpToState = class {
68
- constructor(index) {
69
- this.index = index;
70
- this.type = JUMP_TO_STATE;
71
- }
72
- };
73
- var JumpToAction = class {
74
- constructor(actionId) {
75
- this.actionId = actionId;
76
- this.type = JUMP_TO_ACTION;
77
- }
78
- };
79
- var ImportState = class {
80
- constructor(nextLiftedState) {
81
- this.nextLiftedState = nextLiftedState;
82
- this.type = IMPORT_STATE;
83
- }
84
- };
85
- var LockChanges = class {
86
- constructor(status) {
87
- this.status = status;
88
- this.type = LOCK_CHANGES;
89
- }
90
- };
91
- var PauseRecording = class {
92
- constructor(status) {
93
- this.status = status;
94
- this.type = PAUSE_RECORDING;
95
- }
96
- };
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, inject, NgZone, Injectable, Inject, makeEnvironmentProviders, NgModule } from '@angular/core';
3
+ import * as i2 from '@ngrx/store';
4
+ import { ActionsSubject, UPDATE, INIT, INITIAL_STATE, StateObservable, ReducerManagerDispatcher } from '@ngrx/store';
5
+ import { EMPTY, Observable, of, merge, queueScheduler, ReplaySubject } from 'rxjs';
6
+ import { share, filter, map, concatMap, timeout, debounceTime, catchError, take, takeUntil, switchMap, skip, observeOn, withLatestFrom, scan } from 'rxjs/operators';
7
+ import { toSignal } from '@angular/core/rxjs-interop';
97
8
 
98
- // src/config.mjs
99
- import { InjectionToken } from "@angular/core";
100
- var StoreDevtoolsConfig = class {
101
- constructor() {
102
- this.maxAge = false;
103
- }
104
- };
105
- var STORE_DEVTOOLS_CONFIG = new InjectionToken("@ngrx/store-devtools Options");
106
- var INITIAL_OPTIONS = new InjectionToken("@ngrx/store-devtools Initial Config");
9
+ const PERFORM_ACTION = 'PERFORM_ACTION';
10
+ const REFRESH = 'REFRESH';
11
+ const RESET = 'RESET';
12
+ const ROLLBACK = 'ROLLBACK';
13
+ const COMMIT = 'COMMIT';
14
+ const SWEEP = 'SWEEP';
15
+ const TOGGLE_ACTION = 'TOGGLE_ACTION';
16
+ const SET_ACTIONS_ACTIVE = 'SET_ACTIONS_ACTIVE';
17
+ const JUMP_TO_STATE = 'JUMP_TO_STATE';
18
+ const JUMP_TO_ACTION = 'JUMP_TO_ACTION';
19
+ const IMPORT_STATE = 'IMPORT_STATE';
20
+ const LOCK_CHANGES = 'LOCK_CHANGES';
21
+ const PAUSE_RECORDING = 'PAUSE_RECORDING';
22
+ class PerformAction {
23
+ constructor(action, timestamp) {
24
+ this.action = action;
25
+ this.timestamp = timestamp;
26
+ this.type = PERFORM_ACTION;
27
+ if (typeof action.type === 'undefined') {
28
+ throw new Error('Actions may not have an undefined "type" property. ' +
29
+ 'Have you misspelled a constant?');
30
+ }
31
+ }
32
+ }
33
+ class Refresh {
34
+ constructor() {
35
+ this.type = REFRESH;
36
+ }
37
+ }
38
+ class Reset {
39
+ constructor(timestamp) {
40
+ this.timestamp = timestamp;
41
+ this.type = RESET;
42
+ }
43
+ }
44
+ class Rollback {
45
+ constructor(timestamp) {
46
+ this.timestamp = timestamp;
47
+ this.type = ROLLBACK;
48
+ }
49
+ }
50
+ class Commit {
51
+ constructor(timestamp) {
52
+ this.timestamp = timestamp;
53
+ this.type = COMMIT;
54
+ }
55
+ }
56
+ class Sweep {
57
+ constructor() {
58
+ this.type = SWEEP;
59
+ }
60
+ }
61
+ class ToggleAction {
62
+ constructor(id) {
63
+ this.id = id;
64
+ this.type = TOGGLE_ACTION;
65
+ }
66
+ }
67
+ class SetActionsActive {
68
+ constructor(start, end, active = true) {
69
+ this.start = start;
70
+ this.end = end;
71
+ this.active = active;
72
+ this.type = SET_ACTIONS_ACTIVE;
73
+ }
74
+ }
75
+ class JumpToState {
76
+ constructor(index) {
77
+ this.index = index;
78
+ this.type = JUMP_TO_STATE;
79
+ }
80
+ }
81
+ class JumpToAction {
82
+ constructor(actionId) {
83
+ this.actionId = actionId;
84
+ this.type = JUMP_TO_ACTION;
85
+ }
86
+ }
87
+ class ImportState {
88
+ constructor(nextLiftedState) {
89
+ this.nextLiftedState = nextLiftedState;
90
+ this.type = IMPORT_STATE;
91
+ }
92
+ }
93
+ class LockChanges {
94
+ constructor(status) {
95
+ this.status = status;
96
+ this.type = LOCK_CHANGES;
97
+ }
98
+ }
99
+ class PauseRecording {
100
+ constructor(status) {
101
+ this.status = status;
102
+ this.type = PAUSE_RECORDING;
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Chrome extension documentation
108
+ * @see https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md
109
+ * Firefox extension documentation
110
+ * @see https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md
111
+ */
112
+ class StoreDevtoolsConfig {
113
+ constructor() {
114
+ /**
115
+ * Maximum allowed actions to be stored in the history tree (default: `false`)
116
+ */
117
+ this.maxAge = false;
118
+ }
119
+ }
120
+ const STORE_DEVTOOLS_CONFIG = new InjectionToken('@ngrx/store-devtools Options');
121
+ /**
122
+ * Used to provide a `StoreDevtoolsConfig` for the store-devtools.
123
+ */
124
+ const INITIAL_OPTIONS = new InjectionToken('@ngrx/store-devtools Initial Config');
107
125
  function noMonitor() {
108
- return null;
126
+ return null;
109
127
  }
110
- var DEFAULT_NAME = "NgRx Store DevTools";
128
+ const DEFAULT_NAME = 'NgRx Store DevTools';
111
129
  function createConfig(optionsInput) {
112
- const DEFAULT_OPTIONS = {
113
- maxAge: false,
114
- monitor: noMonitor,
115
- actionSanitizer: void 0,
116
- stateSanitizer: void 0,
117
- name: DEFAULT_NAME,
118
- serialize: false,
119
- logOnly: false,
120
- autoPause: false,
121
- trace: false,
122
- traceLimit: 75,
123
- // Add all features explicitly. This prevent buggy behavior for
124
- // options like "lock" which might otherwise not show up.
125
- features: {
126
- pause: true,
127
- // Start/pause recording of dispatched actions
128
- lock: true,
129
- // Lock/unlock dispatching actions and side effects
130
- persist: true,
131
- // Persist states on page reloading
132
- export: true,
133
- // Export history of actions in a file
134
- import: "custom",
135
- // Import history of actions from a file
136
- jump: true,
137
- // Jump back and forth (time travelling)
138
- skip: true,
139
- // Skip (cancel) actions
140
- reorder: true,
141
- // Drag and drop actions in the history list
142
- dispatch: true,
143
- // Dispatch custom actions or action creators
144
- test: true
145
- // Generate tests for the selected actions
146
- },
147
- connectInZone: false
148
- };
149
- const options = typeof optionsInput === "function" ? optionsInput() : optionsInput;
150
- const logOnly = options.logOnly ? { pause: true, export: true, test: true } : false;
151
- const features = options.features || logOnly || DEFAULT_OPTIONS.features;
152
- if (features.import === true) {
153
- features.import = "custom";
154
- }
155
- const config = Object.assign({}, DEFAULT_OPTIONS, { features }, options);
156
- if (config.maxAge && config.maxAge < 2) {
157
- throw new Error(`Devtools 'maxAge' cannot be less than 2, got ${config.maxAge}`);
158
- }
159
- return config;
130
+ const DEFAULT_OPTIONS = {
131
+ maxAge: false,
132
+ monitor: noMonitor,
133
+ actionSanitizer: undefined,
134
+ stateSanitizer: undefined,
135
+ name: DEFAULT_NAME,
136
+ serialize: false,
137
+ logOnly: false,
138
+ autoPause: false,
139
+ trace: false,
140
+ traceLimit: 75,
141
+ // Add all features explicitly. This prevent buggy behavior for
142
+ // options like "lock" which might otherwise not show up.
143
+ features: {
144
+ pause: true, // Start/pause recording of dispatched actions
145
+ lock: true, // Lock/unlock dispatching actions and side effects
146
+ persist: true, // Persist states on page reloading
147
+ export: true, // Export history of actions in a file
148
+ import: 'custom', // Import history of actions from a file
149
+ jump: true, // Jump back and forth (time travelling)
150
+ skip: true, // Skip (cancel) actions
151
+ reorder: true, // Drag and drop actions in the history list
152
+ dispatch: true, // Dispatch custom actions or action creators
153
+ test: true, // Generate tests for the selected actions
154
+ },
155
+ connectInZone: false,
156
+ };
157
+ const options = typeof optionsInput === 'function' ? optionsInput() : optionsInput;
158
+ const logOnly = options.logOnly
159
+ ? { pause: true, export: true, test: true }
160
+ : false;
161
+ const features = options.features ||
162
+ logOnly ||
163
+ DEFAULT_OPTIONS.features;
164
+ if (features.import === true) {
165
+ features.import = 'custom';
166
+ }
167
+ const config = Object.assign({}, DEFAULT_OPTIONS, { features }, options);
168
+ if (config.maxAge && config.maxAge < 2) {
169
+ throw new Error(`Devtools 'maxAge' cannot be less than 2, got ${config.maxAge}`);
170
+ }
171
+ return config;
160
172
  }
161
173
 
162
- // src/utils.mjs
163
174
  function difference(first, second) {
164
- return first.filter((item) => second.indexOf(item) < 0);
175
+ return first.filter((item) => second.indexOf(item) < 0);
165
176
  }
177
+ /**
178
+ * Provides an app's view into the state of the lifted store.
179
+ */
166
180
  function unliftState(liftedState) {
167
- const { computedStates, currentStateIndex } = liftedState;
168
- if (currentStateIndex >= computedStates.length) {
169
- const { state: state2 } = computedStates[computedStates.length - 1];
170
- return state2;
171
- }
172
- const { state } = computedStates[currentStateIndex];
173
- return state;
181
+ const { computedStates, currentStateIndex } = liftedState;
182
+ // At start up NgRx dispatches init actions,
183
+ // When these init actions are being filtered out by the predicate or safe/block list options
184
+ // we don't have a complete computed states yet.
185
+ // At this point it could happen that we're out of bounds, when this happens we fall back to the last known state
186
+ if (currentStateIndex >= computedStates.length) {
187
+ const { state } = computedStates[computedStates.length - 1];
188
+ return state;
189
+ }
190
+ const { state } = computedStates[currentStateIndex];
191
+ return state;
174
192
  }
175
193
  function unliftAction(liftedState) {
176
- return liftedState.actionsById[liftedState.nextActionId - 1];
194
+ return liftedState.actionsById[liftedState.nextActionId - 1];
177
195
  }
196
+ /**
197
+ * Lifts an app's action into an action on the lifted store.
198
+ */
178
199
  function liftAction(action) {
179
- return new PerformAction(action, +Date.now());
200
+ return new PerformAction(action, +Date.now());
180
201
  }
202
+ /**
203
+ * Sanitizes given actions with given function.
204
+ */
181
205
  function sanitizeActions(actionSanitizer, actions) {
182
- return Object.keys(actions).reduce((sanitizedActions, actionIdx) => {
183
- const idx = Number(actionIdx);
184
- sanitizedActions[idx] = sanitizeAction(actionSanitizer, actions[idx], idx);
185
- return sanitizedActions;
186
- }, {});
206
+ return Object.keys(actions).reduce((sanitizedActions, actionIdx) => {
207
+ const idx = Number(actionIdx);
208
+ sanitizedActions[idx] = sanitizeAction(actionSanitizer, actions[idx], idx);
209
+ return sanitizedActions;
210
+ }, {});
187
211
  }
212
+ /**
213
+ * Sanitizes given action with given function.
214
+ */
188
215
  function sanitizeAction(actionSanitizer, action, actionIdx) {
189
- return {
190
- ...action,
191
- action: actionSanitizer(action.action, actionIdx)
192
- };
216
+ return {
217
+ ...action,
218
+ action: actionSanitizer(action.action, actionIdx),
219
+ };
193
220
  }
221
+ /**
222
+ * Sanitizes given states with given function.
223
+ */
194
224
  function sanitizeStates(stateSanitizer, states) {
195
- return states.map((computedState, idx) => ({
196
- state: sanitizeState(stateSanitizer, computedState.state, idx),
197
- error: computedState.error
198
- }));
225
+ return states.map((computedState, idx) => ({
226
+ state: sanitizeState(stateSanitizer, computedState.state, idx),
227
+ error: computedState.error,
228
+ }));
199
229
  }
230
+ /**
231
+ * Sanitizes given state with given function.
232
+ */
200
233
  function sanitizeState(stateSanitizer, state, stateIdx) {
201
- return stateSanitizer(state, stateIdx);
234
+ return stateSanitizer(state, stateIdx);
202
235
  }
236
+ /**
237
+ * Read the config and tell if actions should be filtered
238
+ */
203
239
  function shouldFilterActions(config) {
204
- return config.predicate || config.actionsSafelist || config.actionsBlocklist;
240
+ return config.predicate || config.actionsSafelist || config.actionsBlocklist;
205
241
  }
242
+ /**
243
+ * Return a full filtered lifted state
244
+ */
206
245
  function filterLiftedState(liftedState, predicate, safelist, blocklist) {
207
- const filteredStagedActionIds = [];
208
- const filteredActionsById = {};
209
- const filteredComputedStates = [];
210
- liftedState.stagedActionIds.forEach((id, idx) => {
211
- const liftedAction = liftedState.actionsById[id];
212
- if (!liftedAction)
213
- return;
214
- if (idx && isActionFiltered(liftedState.computedStates[idx], liftedAction, predicate, safelist, blocklist)) {
215
- return;
216
- }
217
- filteredActionsById[id] = liftedAction;
218
- filteredStagedActionIds.push(id);
219
- filteredComputedStates.push(liftedState.computedStates[idx]);
220
- });
221
- return {
222
- ...liftedState,
223
- stagedActionIds: filteredStagedActionIds,
224
- actionsById: filteredActionsById,
225
- computedStates: filteredComputedStates
226
- };
246
+ const filteredStagedActionIds = [];
247
+ const filteredActionsById = {};
248
+ const filteredComputedStates = [];
249
+ liftedState.stagedActionIds.forEach((id, idx) => {
250
+ const liftedAction = liftedState.actionsById[id];
251
+ if (!liftedAction)
252
+ return;
253
+ if (idx &&
254
+ isActionFiltered(liftedState.computedStates[idx], liftedAction, predicate, safelist, blocklist)) {
255
+ return;
256
+ }
257
+ filteredActionsById[id] = liftedAction;
258
+ filteredStagedActionIds.push(id);
259
+ filteredComputedStates.push(liftedState.computedStates[idx]);
260
+ });
261
+ return {
262
+ ...liftedState,
263
+ stagedActionIds: filteredStagedActionIds,
264
+ actionsById: filteredActionsById,
265
+ computedStates: filteredComputedStates,
266
+ };
227
267
  }
268
+ /**
269
+ * Return true is the action should be ignored
270
+ */
228
271
  function isActionFiltered(state, action, predicate, safelist, blockedlist) {
229
- const predicateMatch = predicate && !predicate(state, action.action);
230
- const safelistMatch = safelist && !action.action.type.match(safelist.map((s) => escapeRegExp(s)).join("|"));
231
- const blocklistMatch = blockedlist && action.action.type.match(blockedlist.map((s) => escapeRegExp(s)).join("|"));
232
- return predicateMatch || safelistMatch || blocklistMatch;
272
+ const predicateMatch = predicate && !predicate(state, action.action);
273
+ const safelistMatch = safelist &&
274
+ !action.action.type.match(safelist.map((s) => escapeRegExp(s)).join('|'));
275
+ const blocklistMatch = blockedlist &&
276
+ action.action.type.match(blockedlist.map((s) => escapeRegExp(s)).join('|'));
277
+ return predicateMatch || safelistMatch || blocklistMatch;
233
278
  }
279
+ /**
280
+ * Return string with escaped RegExp special characters
281
+ * https://stackoverflow.com/a/6969486/1337347
282
+ */
234
283
  function escapeRegExp(s) {
235
- return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
284
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
236
285
  }
237
286
 
238
- // src/zone-config.mjs
239
- import { NgZone, inject } from "@angular/core";
240
287
  function injectZoneConfig(connectInZone) {
241
- const ngZone = connectInZone ? inject(NgZone) : null;
242
- return { ngZone, connectInZone };
288
+ const ngZone = connectInZone ? inject(NgZone) : null;
289
+ return { ngZone, connectInZone };
243
290
  }
244
291
 
245
- // src/devtools-dispatcher.mjs
246
- import { ActionsSubject } from "@ngrx/store";
247
- import { Injectable } from "@angular/core";
248
- import * as i0 from "@angular/core";
249
- var DevtoolsDispatcher = class _DevtoolsDispatcher extends ActionsSubject {
250
- static {
251
- this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i0, type: _DevtoolsDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
252
- }
253
- static {
254
- this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i0, type: _DevtoolsDispatcher });
255
- }
256
- };
257
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i0, type: DevtoolsDispatcher, decorators: [{
258
- type: Injectable
259
- }] });
292
+ class DevtoolsDispatcher extends ActionsSubject {
293
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsDispatcher, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
294
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsDispatcher }); }
295
+ }
296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsDispatcher, decorators: [{
297
+ type: Injectable
298
+ }] });
260
299
 
261
- // src/extension.mjs
262
- import { Inject, Injectable as Injectable2, InjectionToken as InjectionToken2 } from "@angular/core";
263
- import { UPDATE } from "@ngrx/store";
264
- import { EMPTY, Observable, of } from "rxjs";
265
- import { catchError, concatMap, debounceTime, filter, map, share, switchMap, take, takeUntil, timeout } from "rxjs/operators";
266
- import * as i02 from "@angular/core";
267
- var ExtensionActionTypes = {
268
- START: "START",
269
- DISPATCH: "DISPATCH",
270
- STOP: "STOP",
271
- ACTION: "ACTION"
300
+ const ExtensionActionTypes = {
301
+ START: 'START',
302
+ DISPATCH: 'DISPATCH',
303
+ STOP: 'STOP',
304
+ ACTION: 'ACTION',
272
305
  };
273
- var REDUX_DEVTOOLS_EXTENSION = new InjectionToken2("@ngrx/store-devtools Redux Devtools Extension");
274
- var DevtoolsExtension = class _DevtoolsExtension {
275
- constructor(devtoolsExtension, config, dispatcher) {
276
- this.config = config;
277
- this.dispatcher = dispatcher;
278
- this.zoneConfig = injectZoneConfig(this.config.connectInZone);
279
- this.devtoolsExtension = devtoolsExtension;
280
- this.createActionStreams();
281
- }
282
- notify(action, state) {
283
- if (!this.devtoolsExtension) {
284
- return;
285
- }
286
- if (action.type === PERFORM_ACTION) {
287
- if (state.isLocked || state.isPaused) {
288
- return;
289
- }
290
- const currentState = unliftState(state);
291
- if (shouldFilterActions(this.config) && isActionFiltered(currentState, action, this.config.predicate, this.config.actionsSafelist, this.config.actionsBlocklist)) {
292
- return;
293
- }
294
- const sanitizedState = this.config.stateSanitizer ? sanitizeState(this.config.stateSanitizer, currentState, state.currentStateIndex) : currentState;
295
- const sanitizedAction = this.config.actionSanitizer ? sanitizeAction(this.config.actionSanitizer, action, state.nextActionId) : action;
296
- this.sendToReduxDevtools(() => this.extensionConnection.send(sanitizedAction, sanitizedState));
297
- } else {
298
- const sanitizedLiftedState = {
299
- ...state,
300
- stagedActionIds: state.stagedActionIds,
301
- actionsById: this.config.actionSanitizer ? sanitizeActions(this.config.actionSanitizer, state.actionsById) : state.actionsById,
302
- computedStates: this.config.stateSanitizer ? sanitizeStates(this.config.stateSanitizer, state.computedStates) : state.computedStates
303
- };
304
- this.sendToReduxDevtools(() => this.devtoolsExtension.send(null, sanitizedLiftedState, this.getExtensionConfig(this.config)));
305
- }
306
- }
307
- createChangesObservable() {
308
- if (!this.devtoolsExtension) {
309
- return EMPTY;
310
- }
311
- return new Observable((subscriber) => {
312
- const connection = this.zoneConfig.connectInZone ? (
313
- // To reduce change detection cycles, we need to run the `connect` method
314
- // outside of the Angular zone. The `connect` method adds a `message`
315
- // event listener to communicate with an extension using `window.postMessage`
316
- // and handle message events.
317
- this.zoneConfig.ngZone.runOutsideAngular(() => this.devtoolsExtension.connect(this.getExtensionConfig(this.config)))
318
- ) : this.devtoolsExtension.connect(this.getExtensionConfig(this.config));
319
- this.extensionConnection = connection;
320
- connection.init();
321
- connection.subscribe((change) => subscriber.next(change));
322
- return connection.unsubscribe;
323
- });
324
- }
325
- createActionStreams() {
326
- const changes$ = this.createChangesObservable().pipe(share());
327
- const start$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.START));
328
- const stop$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.STOP));
329
- const liftedActions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.DISPATCH), map((change) => this.unwrapAction(change.payload)), concatMap((action) => {
330
- if (action.type === IMPORT_STATE) {
331
- return this.dispatcher.pipe(filter((action2) => action2.type === UPDATE), timeout(1e3), debounceTime(1e3), map(() => action), catchError(() => of(action)), take(1));
332
- } else {
333
- return of(action);
334
- }
335
- }));
336
- const actions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.ACTION), map((change) => this.unwrapAction(change.payload)));
337
- const actionsUntilStop$ = actions$.pipe(takeUntil(stop$));
338
- const liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$));
339
- this.start$ = start$.pipe(takeUntil(stop$));
340
- this.actions$ = this.start$.pipe(switchMap(() => actionsUntilStop$));
341
- this.liftedActions$ = this.start$.pipe(switchMap(() => liftedUntilStop$));
342
- }
343
- unwrapAction(action) {
344
- return typeof action === "string" ? (0, eval)(`(${action})`) : action;
345
- }
346
- getExtensionConfig(config) {
347
- const extensionOptions = {
348
- name: config.name,
349
- features: config.features,
350
- serialize: config.serialize,
351
- autoPause: config.autoPause ?? false,
352
- trace: config.trace ?? false,
353
- traceLimit: config.traceLimit ?? 75
354
- // The action/state sanitizers are not added to the config
355
- // because sanitation is done in this class already.
356
- // It is done before sending it to the devtools extension for consistency:
357
- // - If we call extensionConnection.send(...),
358
- // the extension would call the sanitizers.
359
- // - If we call devtoolsExtension.send(...) (aka full state update),
360
- // the extension would NOT call the sanitizers, so we have to do it ourselves.
361
- };
362
- if (config.maxAge !== false) {
363
- extensionOptions.maxAge = config.maxAge;
306
+ const REDUX_DEVTOOLS_EXTENSION = new InjectionToken('@ngrx/store-devtools Redux Devtools Extension');
307
+ class DevtoolsExtension {
308
+ constructor(devtoolsExtension, config, dispatcher) {
309
+ this.config = config;
310
+ this.dispatcher = dispatcher;
311
+ this.zoneConfig = injectZoneConfig(this.config.connectInZone);
312
+ this.devtoolsExtension = devtoolsExtension;
313
+ this.createActionStreams();
364
314
  }
365
- return extensionOptions;
366
- }
367
- sendToReduxDevtools(send) {
368
- try {
369
- send();
370
- } catch (err) {
371
- console.warn("@ngrx/store-devtools: something went wrong inside the redux devtools", err);
372
- }
373
- }
374
- static {
375
- this.ɵfac = i02.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i02, type: _DevtoolsExtension, deps: [{ token: REDUX_DEVTOOLS_EXTENSION }, { token: STORE_DEVTOOLS_CONFIG }, { token: DevtoolsDispatcher }], target: i02.ɵɵFactoryTarget.Injectable });
376
- }
377
- static {
378
- this.ɵprov = i02.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i02, type: _DevtoolsExtension });
379
- }
380
- };
381
- i02.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i02, type: DevtoolsExtension, decorators: [{
382
- type: Injectable2
383
- }], ctorParameters: () => [{ type: void 0, decorators: [{
384
- type: Inject,
385
- args: [REDUX_DEVTOOLS_EXTENSION]
386
- }] }, { type: StoreDevtoolsConfig, decorators: [{
387
- type: Inject,
388
- args: [STORE_DEVTOOLS_CONFIG]
389
- }] }, { type: DevtoolsDispatcher }] });
315
+ notify(action, state) {
316
+ if (!this.devtoolsExtension) {
317
+ return;
318
+ }
319
+ // Check to see if the action requires a full update of the liftedState.
320
+ // If it is a simple action generated by the user's app and the recording
321
+ // is not locked/paused, only send the action and the current state (fast).
322
+ //
323
+ // A full liftedState update (slow: serializes the entire liftedState) is
324
+ // only required when:
325
+ // a) redux-devtools-extension fires the @@Init action (ignored by
326
+ // @ngrx/store-devtools)
327
+ // b) an action is generated by an @ngrx module (e.g. @ngrx/effects/init
328
+ // or @ngrx/store/update-reducers)
329
+ // c) the state has been recomputed due to time-traveling
330
+ // d) any action that is not a PerformAction to err on the side of
331
+ // caution.
332
+ if (action.type === PERFORM_ACTION) {
333
+ if (state.isLocked || state.isPaused) {
334
+ return;
335
+ }
336
+ const currentState = unliftState(state);
337
+ if (shouldFilterActions(this.config) &&
338
+ isActionFiltered(currentState, action, this.config.predicate, this.config.actionsSafelist, this.config.actionsBlocklist)) {
339
+ return;
340
+ }
341
+ const sanitizedState = this.config.stateSanitizer
342
+ ? sanitizeState(this.config.stateSanitizer, currentState, state.currentStateIndex)
343
+ : currentState;
344
+ const sanitizedAction = this.config.actionSanitizer
345
+ ? sanitizeAction(this.config.actionSanitizer, action, state.nextActionId)
346
+ : action;
347
+ this.sendToReduxDevtools(() => this.extensionConnection.send(sanitizedAction, sanitizedState));
348
+ }
349
+ else {
350
+ // Requires full state update
351
+ const sanitizedLiftedState = {
352
+ ...state,
353
+ stagedActionIds: state.stagedActionIds,
354
+ actionsById: this.config.actionSanitizer
355
+ ? sanitizeActions(this.config.actionSanitizer, state.actionsById)
356
+ : state.actionsById,
357
+ computedStates: this.config.stateSanitizer
358
+ ? sanitizeStates(this.config.stateSanitizer, state.computedStates)
359
+ : state.computedStates,
360
+ };
361
+ this.sendToReduxDevtools(() => this.devtoolsExtension.send(null, sanitizedLiftedState, this.getExtensionConfig(this.config)));
362
+ }
363
+ }
364
+ createChangesObservable() {
365
+ if (!this.devtoolsExtension) {
366
+ return EMPTY;
367
+ }
368
+ return new Observable((subscriber) => {
369
+ const connection = this.zoneConfig.connectInZone
370
+ ? // To reduce change detection cycles, we need to run the `connect` method
371
+ // outside of the Angular zone. The `connect` method adds a `message`
372
+ // event listener to communicate with an extension using `window.postMessage`
373
+ // and handle message events.
374
+ this.zoneConfig.ngZone.runOutsideAngular(() => this.devtoolsExtension.connect(this.getExtensionConfig(this.config)))
375
+ : this.devtoolsExtension.connect(this.getExtensionConfig(this.config));
376
+ this.extensionConnection = connection;
377
+ connection.init();
378
+ connection.subscribe((change) => subscriber.next(change));
379
+ return connection.unsubscribe;
380
+ });
381
+ }
382
+ createActionStreams() {
383
+ // Listens to all changes
384
+ const changes$ = this.createChangesObservable().pipe(share());
385
+ // Listen for the start action
386
+ const start$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.START));
387
+ // Listen for the stop action
388
+ const stop$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.STOP));
389
+ // Listen for lifted actions
390
+ const liftedActions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.DISPATCH), map((change) => this.unwrapAction(change.payload)), concatMap((action) => {
391
+ if (action.type === IMPORT_STATE) {
392
+ // State imports may happen in two situations:
393
+ // 1. Explicitly by user
394
+ // 2. User activated the "persist state accross reloads" option
395
+ // and now the state is imported during reload.
396
+ // Because of option 2, we need to give possible
397
+ // lazy loaded reducers time to instantiate.
398
+ // As soon as there is no UPDATE action within 1 second,
399
+ // it is assumed that all reducers are loaded.
400
+ return this.dispatcher.pipe(filter((action) => action.type === UPDATE), timeout(1000), debounceTime(1000), map(() => action), catchError(() => of(action)), take(1));
401
+ }
402
+ else {
403
+ return of(action);
404
+ }
405
+ }));
406
+ // Listen for unlifted actions
407
+ const actions$ = changes$.pipe(filter((change) => change.type === ExtensionActionTypes.ACTION), map((change) => this.unwrapAction(change.payload)));
408
+ const actionsUntilStop$ = actions$.pipe(takeUntil(stop$));
409
+ const liftedUntilStop$ = liftedActions$.pipe(takeUntil(stop$));
410
+ this.start$ = start$.pipe(takeUntil(stop$));
411
+ // Only take the action sources between the start/stop events
412
+ this.actions$ = this.start$.pipe(switchMap(() => actionsUntilStop$));
413
+ this.liftedActions$ = this.start$.pipe(switchMap(() => liftedUntilStop$));
414
+ }
415
+ unwrapAction(action) {
416
+ // indirect eval according to https://esbuild.github.io/content-types/#direct-eval
417
+ return typeof action === 'string' ? (0, eval)(`(${action})`) : action;
418
+ }
419
+ getExtensionConfig(config) {
420
+ const extensionOptions = {
421
+ name: config.name,
422
+ features: config.features,
423
+ serialize: config.serialize,
424
+ autoPause: config.autoPause ?? false,
425
+ trace: config.trace ?? false,
426
+ traceLimit: config.traceLimit ?? 75,
427
+ // The action/state sanitizers are not added to the config
428
+ // because sanitation is done in this class already.
429
+ // It is done before sending it to the devtools extension for consistency:
430
+ // - If we call extensionConnection.send(...),
431
+ // the extension would call the sanitizers.
432
+ // - If we call devtoolsExtension.send(...) (aka full state update),
433
+ // the extension would NOT call the sanitizers, so we have to do it ourselves.
434
+ };
435
+ if (config.maxAge !== false /* support === 0 */) {
436
+ extensionOptions.maxAge = config.maxAge;
437
+ }
438
+ return extensionOptions;
439
+ }
440
+ sendToReduxDevtools(send) {
441
+ try {
442
+ send();
443
+ }
444
+ catch (err) {
445
+ console.warn('@ngrx/store-devtools: something went wrong inside the redux devtools', err);
446
+ }
447
+ }
448
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsExtension, deps: [{ token: REDUX_DEVTOOLS_EXTENSION }, { token: STORE_DEVTOOLS_CONFIG }, { token: DevtoolsDispatcher }], target: i0.ɵɵFactoryTarget.Injectable }); }
449
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsExtension }); }
450
+ }
451
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: DevtoolsExtension, decorators: [{
452
+ type: Injectable
453
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
454
+ type: Inject,
455
+ args: [REDUX_DEVTOOLS_EXTENSION]
456
+ }] }, { type: StoreDevtoolsConfig, decorators: [{
457
+ type: Inject,
458
+ args: [STORE_DEVTOOLS_CONFIG]
459
+ }] }, { type: DevtoolsDispatcher }] });
390
460
 
391
- // src/reducer.mjs
392
- import { UPDATE as UPDATE2, INIT } from "@ngrx/store";
393
- var INIT_ACTION = { type: INIT };
394
- var RECOMPUTE = "@ngrx/store-devtools/recompute";
395
- var RECOMPUTE_ACTION = { type: RECOMPUTE };
461
+ const INIT_ACTION = { type: INIT };
462
+ const RECOMPUTE = '@ngrx/store-devtools/recompute';
463
+ const RECOMPUTE_ACTION = { type: RECOMPUTE };
464
+ /**
465
+ * Computes the next entry in the log by applying an action.
466
+ */
396
467
  function computeNextEntry(reducer, action, state, error, errorHandler) {
397
- if (error) {
468
+ if (error) {
469
+ return {
470
+ state,
471
+ error: 'Interrupted by an error up the chain',
472
+ };
473
+ }
474
+ let nextState = state;
475
+ let nextError;
476
+ try {
477
+ nextState = reducer(state, action);
478
+ }
479
+ catch (err) {
480
+ nextError = err.toString();
481
+ errorHandler.handleError(err);
482
+ }
398
483
  return {
399
- state,
400
- error: "Interrupted by an error up the chain"
484
+ state: nextState,
485
+ error: nextError,
401
486
  };
402
- }
403
- let nextState = state;
404
- let nextError;
405
- try {
406
- nextState = reducer(state, action);
407
- } catch (err) {
408
- nextError = err.toString();
409
- errorHandler.handleError(err);
410
- }
411
- return {
412
- state: nextState,
413
- error: nextError
414
- };
415
487
  }
488
+ /**
489
+ * Runs the reducer on invalidated actions to get a fresh computation log.
490
+ */
416
491
  function recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused) {
417
- if (minInvalidatedStateIndex >= computedStates.length && computedStates.length === stagedActionIds.length) {
418
- return computedStates;
419
- }
420
- const nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex);
421
- const lastIncludedActionId = stagedActionIds.length - (isPaused ? 1 : 0);
422
- for (let i = minInvalidatedStateIndex; i < lastIncludedActionId; i++) {
423
- const actionId = stagedActionIds[i];
424
- const action = actionsById[actionId].action;
425
- const previousEntry = nextComputedStates[i - 1];
426
- const previousState = previousEntry ? previousEntry.state : committedState;
427
- const previousError = previousEntry ? previousEntry.error : void 0;
428
- const shouldSkip = skippedActionIds.indexOf(actionId) > -1;
429
- const entry = shouldSkip ? previousEntry : computeNextEntry(reducer, action, previousState, previousError, errorHandler);
430
- nextComputedStates.push(entry);
431
- }
432
- if (isPaused) {
433
- nextComputedStates.push(computedStates[computedStates.length - 1]);
434
- }
435
- return nextComputedStates;
492
+ // Optimization: exit early and return the same reference
493
+ // if we know nothing could have changed.
494
+ if (minInvalidatedStateIndex >= computedStates.length &&
495
+ computedStates.length === stagedActionIds.length) {
496
+ return computedStates;
497
+ }
498
+ const nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex);
499
+ // If the recording is paused, recompute all states up until the pause state,
500
+ // else recompute all states.
501
+ const lastIncludedActionId = stagedActionIds.length - (isPaused ? 1 : 0);
502
+ for (let i = minInvalidatedStateIndex; i < lastIncludedActionId; i++) {
503
+ const actionId = stagedActionIds[i];
504
+ const action = actionsById[actionId].action;
505
+ const previousEntry = nextComputedStates[i - 1];
506
+ const previousState = previousEntry ? previousEntry.state : committedState;
507
+ const previousError = previousEntry ? previousEntry.error : undefined;
508
+ const shouldSkip = skippedActionIds.indexOf(actionId) > -1;
509
+ const entry = shouldSkip
510
+ ? previousEntry
511
+ : computeNextEntry(reducer, action, previousState, previousError, errorHandler);
512
+ nextComputedStates.push(entry);
513
+ }
514
+ // If the recording is paused, the last state will not be recomputed,
515
+ // because it's essentially not part of the state history.
516
+ if (isPaused) {
517
+ nextComputedStates.push(computedStates[computedStates.length - 1]);
518
+ }
519
+ return nextComputedStates;
436
520
  }
437
521
  function liftInitialState(initialCommittedState, monitorReducer) {
438
- return {
439
- monitorState: monitorReducer(void 0, {}),
440
- nextActionId: 1,
441
- actionsById: { 0: liftAction(INIT_ACTION) },
442
- stagedActionIds: [0],
443
- skippedActionIds: [],
444
- committedState: initialCommittedState,
445
- currentStateIndex: 0,
446
- computedStates: [],
447
- isLocked: false,
448
- isPaused: false
449
- };
522
+ return {
523
+ monitorState: monitorReducer(undefined, {}),
524
+ nextActionId: 1,
525
+ actionsById: { 0: liftAction(INIT_ACTION) },
526
+ stagedActionIds: [0],
527
+ skippedActionIds: [],
528
+ committedState: initialCommittedState,
529
+ currentStateIndex: 0,
530
+ computedStates: [],
531
+ isLocked: false,
532
+ isPaused: false,
533
+ };
450
534
  }
535
+ /**
536
+ * Creates a history state reducer from an app's reducer.
537
+ */
451
538
  function liftReducerWith(initialCommittedState, initialLiftedState, errorHandler, monitorReducer, options = {}) {
452
- return (reducer) => (liftedState, liftedAction) => {
453
- let { monitorState, actionsById, nextActionId, stagedActionIds, skippedActionIds, committedState, currentStateIndex, computedStates, isLocked, isPaused } = liftedState || initialLiftedState;
454
- if (!liftedState) {
455
- actionsById = Object.create(actionsById);
456
- }
457
- function commitExcessActions(n) {
458
- let excess = n;
459
- let idsToDelete = stagedActionIds.slice(1, excess + 1);
460
- for (let i = 0; i < idsToDelete.length; i++) {
461
- if (computedStates[i + 1].error) {
462
- excess = i;
463
- idsToDelete = stagedActionIds.slice(1, excess + 1);
464
- break;
465
- } else {
466
- delete actionsById[idsToDelete[i]];
467
- }
468
- }
469
- skippedActionIds = skippedActionIds.filter((id) => idsToDelete.indexOf(id) === -1);
470
- stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)];
471
- committedState = computedStates[excess].state;
472
- computedStates = computedStates.slice(excess);
473
- currentStateIndex = currentStateIndex > excess ? currentStateIndex - excess : 0;
474
- }
475
- function commitChanges() {
476
- actionsById = { 0: liftAction(INIT_ACTION) };
477
- nextActionId = 1;
478
- stagedActionIds = [0];
479
- skippedActionIds = [];
480
- committedState = computedStates[currentStateIndex].state;
481
- currentStateIndex = 0;
482
- computedStates = [];
483
- }
484
- let minInvalidatedStateIndex = 0;
485
- switch (liftedAction.type) {
486
- case LOCK_CHANGES: {
487
- isLocked = liftedAction.status;
488
- minInvalidatedStateIndex = Infinity;
489
- break;
490
- }
491
- case PAUSE_RECORDING: {
492
- isPaused = liftedAction.status;
493
- if (isPaused) {
494
- stagedActionIds = [...stagedActionIds, nextActionId];
495
- actionsById[nextActionId] = new PerformAction({
496
- type: "@ngrx/devtools/pause"
497
- }, +Date.now());
498
- nextActionId++;
499
- minInvalidatedStateIndex = stagedActionIds.length - 1;
500
- computedStates = computedStates.concat(computedStates[computedStates.length - 1]);
501
- if (currentStateIndex === stagedActionIds.length - 2) {
502
- currentStateIndex++;
503
- }
504
- minInvalidatedStateIndex = Infinity;
505
- } else {
506
- commitChanges();
507
- }
508
- break;
509
- }
510
- case RESET: {
511
- actionsById = { 0: liftAction(INIT_ACTION) };
512
- nextActionId = 1;
513
- stagedActionIds = [0];
514
- skippedActionIds = [];
515
- committedState = initialCommittedState;
516
- currentStateIndex = 0;
517
- computedStates = [];
518
- break;
519
- }
520
- case COMMIT: {
521
- commitChanges();
522
- break;
523
- }
524
- case ROLLBACK: {
525
- actionsById = { 0: liftAction(INIT_ACTION) };
526
- nextActionId = 1;
527
- stagedActionIds = [0];
528
- skippedActionIds = [];
529
- currentStateIndex = 0;
530
- computedStates = [];
531
- break;
532
- }
533
- case TOGGLE_ACTION: {
534
- const { id: actionId } = liftedAction;
535
- const index = skippedActionIds.indexOf(actionId);
536
- if (index === -1) {
537
- skippedActionIds = [actionId, ...skippedActionIds];
538
- } else {
539
- skippedActionIds = skippedActionIds.filter((id) => id !== actionId);
540
- }
541
- minInvalidatedStateIndex = stagedActionIds.indexOf(actionId);
542
- break;
543
- }
544
- case SET_ACTIONS_ACTIVE: {
545
- const { start, end, active } = liftedAction;
546
- const actionIds = [];
547
- for (let i = start; i < end; i++)
548
- actionIds.push(i);
549
- if (active) {
550
- skippedActionIds = difference(skippedActionIds, actionIds);
551
- } else {
552
- skippedActionIds = [...skippedActionIds, ...actionIds];
539
+ /**
540
+ * Manages how the history actions modify the history state.
541
+ */
542
+ return (reducer) => (liftedState, liftedAction) => {
543
+ let { monitorState, actionsById, nextActionId, stagedActionIds, skippedActionIds, committedState, currentStateIndex, computedStates, isLocked, isPaused, } = liftedState || initialLiftedState;
544
+ if (!liftedState) {
545
+ // Prevent mutating initialLiftedState
546
+ actionsById = Object.create(actionsById);
553
547
  }
554
- minInvalidatedStateIndex = stagedActionIds.indexOf(start);
555
- break;
556
- }
557
- case JUMP_TO_STATE: {
558
- currentStateIndex = liftedAction.index;
559
- minInvalidatedStateIndex = Infinity;
560
- break;
561
- }
562
- case JUMP_TO_ACTION: {
563
- const index = stagedActionIds.indexOf(liftedAction.actionId);
564
- if (index !== -1)
565
- currentStateIndex = index;
566
- minInvalidatedStateIndex = Infinity;
567
- break;
568
- }
569
- case SWEEP: {
570
- stagedActionIds = difference(stagedActionIds, skippedActionIds);
571
- skippedActionIds = [];
572
- currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1);
573
- break;
574
- }
575
- case PERFORM_ACTION: {
576
- if (isLocked) {
577
- return liftedState || initialLiftedState;
578
- }
579
- if (isPaused || liftedState && isActionFiltered(liftedState.computedStates[currentStateIndex], liftedAction, options.predicate, options.actionsSafelist, options.actionsBlocklist)) {
580
- const lastState = computedStates[computedStates.length - 1];
581
- computedStates = [
582
- ...computedStates.slice(0, -1),
583
- computeNextEntry(reducer, liftedAction.action, lastState.state, lastState.error, errorHandler)
584
- ];
585
- minInvalidatedStateIndex = Infinity;
586
- break;
587
- }
588
- if (options.maxAge && stagedActionIds.length === options.maxAge) {
589
- commitExcessActions(1);
590
- }
591
- if (currentStateIndex === stagedActionIds.length - 1) {
592
- currentStateIndex++;
548
+ function commitExcessActions(n) {
549
+ // Auto-commits n-number of excess actions.
550
+ let excess = n;
551
+ let idsToDelete = stagedActionIds.slice(1, excess + 1);
552
+ for (let i = 0; i < idsToDelete.length; i++) {
553
+ if (computedStates[i + 1].error) {
554
+ // Stop if error is found. Commit actions up to error.
555
+ excess = i;
556
+ idsToDelete = stagedActionIds.slice(1, excess + 1);
557
+ break;
558
+ }
559
+ else {
560
+ delete actionsById[idsToDelete[i]];
561
+ }
562
+ }
563
+ skippedActionIds = skippedActionIds.filter((id) => idsToDelete.indexOf(id) === -1);
564
+ stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)];
565
+ committedState = computedStates[excess].state;
566
+ computedStates = computedStates.slice(excess);
567
+ currentStateIndex =
568
+ currentStateIndex > excess ? currentStateIndex - excess : 0;
593
569
  }
594
- const actionId = nextActionId++;
595
- actionsById[actionId] = liftedAction;
596
- stagedActionIds = [...stagedActionIds, actionId];
597
- minInvalidatedStateIndex = stagedActionIds.length - 1;
598
- break;
599
- }
600
- case IMPORT_STATE: {
601
- ({
602
- monitorState,
603
- actionsById,
604
- nextActionId,
605
- stagedActionIds,
606
- skippedActionIds,
607
- committedState,
608
- currentStateIndex,
609
- computedStates,
610
- isLocked,
611
- isPaused
612
- } = liftedAction.nextLiftedState);
613
- break;
614
- }
615
- case INIT: {
616
- minInvalidatedStateIndex = 0;
617
- if (options.maxAge && stagedActionIds.length > options.maxAge) {
618
- computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
619
- commitExcessActions(stagedActionIds.length - options.maxAge);
620
- minInvalidatedStateIndex = Infinity;
570
+ function commitChanges() {
571
+ // Consider the last committed state the new starting point.
572
+ // Squash any staged actions into a single committed state.
573
+ actionsById = { 0: liftAction(INIT_ACTION) };
574
+ nextActionId = 1;
575
+ stagedActionIds = [0];
576
+ skippedActionIds = [];
577
+ committedState = computedStates[currentStateIndex].state;
578
+ currentStateIndex = 0;
579
+ computedStates = [];
621
580
  }
622
- break;
623
- }
624
- case UPDATE2: {
625
- const stateHasErrors = computedStates.filter((state) => state.error).length > 0;
626
- if (stateHasErrors) {
627
- minInvalidatedStateIndex = 0;
628
- if (options.maxAge && stagedActionIds.length > options.maxAge) {
629
- computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
630
- commitExcessActions(stagedActionIds.length - options.maxAge);
631
- minInvalidatedStateIndex = Infinity;
632
- }
633
- } else {
634
- if (!isPaused && !isLocked) {
635
- if (currentStateIndex === stagedActionIds.length - 1) {
636
- currentStateIndex++;
581
+ // By default, aggressively recompute every state whatever happens.
582
+ // This has O(n) performance, so we'll override this to a sensible
583
+ // value whenever we feel like we don't have to recompute the states.
584
+ let minInvalidatedStateIndex = 0;
585
+ switch (liftedAction.type) {
586
+ case LOCK_CHANGES: {
587
+ isLocked = liftedAction.status;
588
+ minInvalidatedStateIndex = Infinity;
589
+ break;
590
+ }
591
+ case PAUSE_RECORDING: {
592
+ isPaused = liftedAction.status;
593
+ if (isPaused) {
594
+ // Add a pause action to signal the devtools-user the recording is paused.
595
+ // The corresponding state will be overwritten on each update to always contain
596
+ // the latest state (see Actions.PERFORM_ACTION).
597
+ stagedActionIds = [...stagedActionIds, nextActionId];
598
+ actionsById[nextActionId] = new PerformAction({
599
+ type: '@ngrx/devtools/pause',
600
+ }, +Date.now());
601
+ nextActionId++;
602
+ minInvalidatedStateIndex = stagedActionIds.length - 1;
603
+ computedStates = computedStates.concat(computedStates[computedStates.length - 1]);
604
+ if (currentStateIndex === stagedActionIds.length - 2) {
605
+ currentStateIndex++;
606
+ }
607
+ minInvalidatedStateIndex = Infinity;
608
+ }
609
+ else {
610
+ commitChanges();
611
+ }
612
+ break;
613
+ }
614
+ case RESET: {
615
+ // Get back to the state the store was created with.
616
+ actionsById = { 0: liftAction(INIT_ACTION) };
617
+ nextActionId = 1;
618
+ stagedActionIds = [0];
619
+ skippedActionIds = [];
620
+ committedState = initialCommittedState;
621
+ currentStateIndex = 0;
622
+ computedStates = [];
623
+ break;
624
+ }
625
+ case COMMIT: {
626
+ commitChanges();
627
+ break;
628
+ }
629
+ case ROLLBACK: {
630
+ // Forget about any staged actions.
631
+ // Start again from the last committed state.
632
+ actionsById = { 0: liftAction(INIT_ACTION) };
633
+ nextActionId = 1;
634
+ stagedActionIds = [0];
635
+ skippedActionIds = [];
636
+ currentStateIndex = 0;
637
+ computedStates = [];
638
+ break;
639
+ }
640
+ case TOGGLE_ACTION: {
641
+ // Toggle whether an action with given ID is skipped.
642
+ // Being skipped means it is a no-op during the computation.
643
+ const { id: actionId } = liftedAction;
644
+ const index = skippedActionIds.indexOf(actionId);
645
+ if (index === -1) {
646
+ skippedActionIds = [actionId, ...skippedActionIds];
647
+ }
648
+ else {
649
+ skippedActionIds = skippedActionIds.filter((id) => id !== actionId);
650
+ }
651
+ // Optimization: we know history before this action hasn't changed
652
+ minInvalidatedStateIndex = stagedActionIds.indexOf(actionId);
653
+ break;
654
+ }
655
+ case SET_ACTIONS_ACTIVE: {
656
+ // Toggle whether an action with given ID is skipped.
657
+ // Being skipped means it is a no-op during the computation.
658
+ const { start, end, active } = liftedAction;
659
+ const actionIds = [];
660
+ for (let i = start; i < end; i++)
661
+ actionIds.push(i);
662
+ if (active) {
663
+ skippedActionIds = difference(skippedActionIds, actionIds);
664
+ }
665
+ else {
666
+ skippedActionIds = [...skippedActionIds, ...actionIds];
667
+ }
668
+ // Optimization: we know history before this action hasn't changed
669
+ minInvalidatedStateIndex = stagedActionIds.indexOf(start);
670
+ break;
671
+ }
672
+ case JUMP_TO_STATE: {
673
+ // Without recomputing anything, move the pointer that tell us
674
+ // which state is considered the current one. Useful for sliders.
675
+ currentStateIndex = liftedAction.index;
676
+ // Optimization: we know the history has not changed.
677
+ minInvalidatedStateIndex = Infinity;
678
+ break;
679
+ }
680
+ case JUMP_TO_ACTION: {
681
+ // Jumps to a corresponding state to a specific action.
682
+ // Useful when filtering actions.
683
+ const index = stagedActionIds.indexOf(liftedAction.actionId);
684
+ if (index !== -1)
685
+ currentStateIndex = index;
686
+ minInvalidatedStateIndex = Infinity;
687
+ break;
688
+ }
689
+ case SWEEP: {
690
+ // Forget any actions that are currently being skipped.
691
+ stagedActionIds = difference(stagedActionIds, skippedActionIds);
692
+ skippedActionIds = [];
693
+ currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1);
694
+ break;
695
+ }
696
+ case PERFORM_ACTION: {
697
+ // Ignore action and return state as is if recording is locked
698
+ if (isLocked) {
699
+ return liftedState || initialLiftedState;
700
+ }
701
+ if (isPaused ||
702
+ (liftedState &&
703
+ isActionFiltered(liftedState.computedStates[currentStateIndex], liftedAction, options.predicate, options.actionsSafelist, options.actionsBlocklist))) {
704
+ // If recording is paused or if the action should be ignored, overwrite the last state
705
+ // (corresponds to the pause action) and keep everything else as is.
706
+ // This way, the app gets the new current state while the devtools
707
+ // do not record another action.
708
+ const lastState = computedStates[computedStates.length - 1];
709
+ computedStates = [
710
+ ...computedStates.slice(0, -1),
711
+ computeNextEntry(reducer, liftedAction.action, lastState.state, lastState.error, errorHandler),
712
+ ];
713
+ minInvalidatedStateIndex = Infinity;
714
+ break;
715
+ }
716
+ // Auto-commit as new actions come in.
717
+ if (options.maxAge && stagedActionIds.length === options.maxAge) {
718
+ commitExcessActions(1);
719
+ }
720
+ if (currentStateIndex === stagedActionIds.length - 1) {
721
+ currentStateIndex++;
722
+ }
723
+ const actionId = nextActionId++;
724
+ // Mutation! This is the hottest path, and we optimize on purpose.
725
+ // It is safe because we set a new key in a cache dictionary.
726
+ actionsById[actionId] = liftedAction;
727
+ stagedActionIds = [...stagedActionIds, actionId];
728
+ // Optimization: we know that only the new action needs computing.
729
+ minInvalidatedStateIndex = stagedActionIds.length - 1;
730
+ break;
731
+ }
732
+ case IMPORT_STATE: {
733
+ // Completely replace everything.
734
+ ({
735
+ monitorState,
736
+ actionsById,
737
+ nextActionId,
738
+ stagedActionIds,
739
+ skippedActionIds,
740
+ committedState,
741
+ currentStateIndex,
742
+ computedStates,
743
+ isLocked,
744
+ isPaused,
745
+ } = liftedAction.nextLiftedState);
746
+ break;
747
+ }
748
+ case INIT: {
749
+ // Always recompute states on hot reload and init.
750
+ minInvalidatedStateIndex = 0;
751
+ if (options.maxAge && stagedActionIds.length > options.maxAge) {
752
+ // States must be recomputed before committing excess.
753
+ computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
754
+ commitExcessActions(stagedActionIds.length - options.maxAge);
755
+ // Avoid double computation.
756
+ minInvalidatedStateIndex = Infinity;
757
+ }
758
+ break;
759
+ }
760
+ case UPDATE: {
761
+ const stateHasErrors = computedStates.filter((state) => state.error).length > 0;
762
+ if (stateHasErrors) {
763
+ // Recompute all states
764
+ minInvalidatedStateIndex = 0;
765
+ if (options.maxAge && stagedActionIds.length > options.maxAge) {
766
+ // States must be recomputed before committing excess.
767
+ computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
768
+ commitExcessActions(stagedActionIds.length - options.maxAge);
769
+ // Avoid double computation.
770
+ minInvalidatedStateIndex = Infinity;
771
+ }
772
+ }
773
+ else {
774
+ // If not paused/locked, add a new action to signal devtools-user
775
+ // that there was a reducer update.
776
+ if (!isPaused && !isLocked) {
777
+ if (currentStateIndex === stagedActionIds.length - 1) {
778
+ currentStateIndex++;
779
+ }
780
+ // Add a new action to only recompute state
781
+ const actionId = nextActionId++;
782
+ actionsById[actionId] = new PerformAction(liftedAction, +Date.now());
783
+ stagedActionIds = [...stagedActionIds, actionId];
784
+ minInvalidatedStateIndex = stagedActionIds.length - 1;
785
+ computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
786
+ }
787
+ // Recompute state history with latest reducer and update action
788
+ computedStates = computedStates.map((cmp) => ({
789
+ ...cmp,
790
+ state: reducer(cmp.state, RECOMPUTE_ACTION),
791
+ }));
792
+ currentStateIndex = stagedActionIds.length - 1;
793
+ if (options.maxAge && stagedActionIds.length > options.maxAge) {
794
+ commitExcessActions(stagedActionIds.length - options.maxAge);
795
+ }
796
+ // Avoid double computation.
797
+ minInvalidatedStateIndex = Infinity;
798
+ }
799
+ break;
800
+ }
801
+ default: {
802
+ // If the action is not recognized, it's a monitor action.
803
+ // Optimization: a monitor action can't change history.
804
+ minInvalidatedStateIndex = Infinity;
805
+ break;
637
806
  }
638
- const actionId = nextActionId++;
639
- actionsById[actionId] = new PerformAction(liftedAction, +Date.now());
640
- stagedActionIds = [...stagedActionIds, actionId];
641
- minInvalidatedStateIndex = stagedActionIds.length - 1;
642
- computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
643
- }
644
- computedStates = computedStates.map((cmp) => ({
645
- ...cmp,
646
- state: reducer(cmp.state, RECOMPUTE_ACTION)
647
- }));
648
- currentStateIndex = stagedActionIds.length - 1;
649
- if (options.maxAge && stagedActionIds.length > options.maxAge) {
650
- commitExcessActions(stagedActionIds.length - options.maxAge);
651
- }
652
- minInvalidatedStateIndex = Infinity;
653
807
  }
654
- break;
655
- }
656
- default: {
657
- minInvalidatedStateIndex = Infinity;
658
- break;
659
- }
660
- }
661
- computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
662
- monitorState = monitorReducer(monitorState, liftedAction);
663
- return {
664
- monitorState,
665
- actionsById,
666
- nextActionId,
667
- stagedActionIds,
668
- skippedActionIds,
669
- committedState,
670
- currentStateIndex,
671
- computedStates,
672
- isLocked,
673
- isPaused
808
+ computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, errorHandler, isPaused);
809
+ monitorState = monitorReducer(monitorState, liftedAction);
810
+ return {
811
+ monitorState,
812
+ actionsById,
813
+ nextActionId,
814
+ stagedActionIds,
815
+ skippedActionIds,
816
+ committedState,
817
+ currentStateIndex,
818
+ computedStates,
819
+ isLocked,
820
+ isPaused,
821
+ };
674
822
  };
675
- };
676
823
  }
677
824
 
678
- // src/devtools.mjs
679
- import { Injectable as Injectable3, Inject as Inject2 } from "@angular/core";
680
- import { toSignal } from "@angular/core/rxjs-interop";
681
- import { INITIAL_STATE } from "@ngrx/store";
682
- import { merge, Observable as Observable2, queueScheduler, ReplaySubject } from "rxjs";
683
- import { map as map2, observeOn, scan, skip, withLatestFrom } from "rxjs/operators";
684
- import * as i03 from "@angular/core";
685
- import * as i2 from "@ngrx/store";
686
- var StoreDevtools = class _StoreDevtools {
687
- constructor(dispatcher, actions$, reducers$, extension, scannedActions, errorHandler, initialState, config) {
688
- const liftedInitialState = liftInitialState(initialState, config.monitor);
689
- const liftReducer = liftReducerWith(initialState, liftedInitialState, errorHandler, config.monitor, config);
690
- const liftedAction$ = merge(merge(actions$.asObservable().pipe(skip(1)), extension.actions$).pipe(map2(liftAction)), dispatcher, extension.liftedActions$).pipe(observeOn(queueScheduler));
691
- const liftedReducer$ = reducers$.pipe(map2(liftReducer));
692
- const zoneConfig = injectZoneConfig(config.connectInZone);
693
- const liftedStateSubject = new ReplaySubject(1);
694
- this.liftedStateSubscription = liftedAction$.pipe(
695
- withLatestFrom(liftedReducer$),
696
- // The extension would post messages back outside of the Angular zone
697
- // because we call `connect()` wrapped with `runOutsideAngular`. We run change
698
- // detection only once at the end after all the required asynchronous tasks have
699
- // been processed (for instance, `setInterval` scheduled by the `timeout` operator).
700
- // We have to re-enter the Angular zone before the `scan` since it runs the reducer
701
- // which must be run within the Angular zone.
702
- emitInZone(zoneConfig),
703
- scan(({ state: liftedState }, [action, reducer]) => {
704
- let reducedLiftedState = reducer(liftedState, action);
705
- if (action.type !== PERFORM_ACTION && shouldFilterActions(config)) {
706
- reducedLiftedState = filterLiftedState(reducedLiftedState, config.predicate, config.actionsSafelist, config.actionsBlocklist);
707
- }
708
- extension.notify(action, reducedLiftedState);
709
- return { state: reducedLiftedState, action };
710
- }, { state: liftedInitialState, action: null })
711
- ).subscribe(({ state, action }) => {
712
- liftedStateSubject.next(state);
713
- if (action.type === PERFORM_ACTION) {
714
- const unliftedAction = action.action;
715
- scannedActions.next(unliftedAction);
716
- }
717
- });
718
- this.extensionStartSubscription = extension.start$.pipe(emitInZone(zoneConfig)).subscribe(() => {
719
- this.refresh();
720
- });
721
- const liftedState$ = liftedStateSubject.asObservable();
722
- const state$ = liftedState$.pipe(map2(unliftState));
723
- Object.defineProperty(state$, "state", {
724
- value: toSignal(state$, { manualCleanup: true, requireSync: true })
725
- });
726
- this.dispatcher = dispatcher;
727
- this.liftedState = liftedState$;
728
- this.state = state$;
729
- }
730
- ngOnDestroy() {
731
- this.liftedStateSubscription.unsubscribe();
732
- this.extensionStartSubscription.unsubscribe();
733
- }
734
- dispatch(action) {
735
- this.dispatcher.next(action);
736
- }
737
- next(action) {
738
- this.dispatcher.next(action);
739
- }
740
- error(error) {
741
- }
742
- complete() {
743
- }
744
- performAction(action) {
745
- this.dispatch(new PerformAction(action, +Date.now()));
746
- }
747
- refresh() {
748
- this.dispatch(new Refresh());
749
- }
750
- reset() {
751
- this.dispatch(new Reset(+Date.now()));
752
- }
753
- rollback() {
754
- this.dispatch(new Rollback(+Date.now()));
755
- }
756
- commit() {
757
- this.dispatch(new Commit(+Date.now()));
758
- }
759
- sweep() {
760
- this.dispatch(new Sweep());
761
- }
762
- toggleAction(id) {
763
- this.dispatch(new ToggleAction(id));
764
- }
765
- jumpToAction(actionId) {
766
- this.dispatch(new JumpToAction(actionId));
767
- }
768
- jumpToState(index) {
769
- this.dispatch(new JumpToState(index));
770
- }
771
- importState(nextLiftedState) {
772
- this.dispatch(new ImportState(nextLiftedState));
773
- }
774
- lockChanges(status) {
775
- this.dispatch(new LockChanges(status));
776
- }
777
- pauseRecording(status) {
778
- this.dispatch(new PauseRecording(status));
779
- }
780
- static {
781
- this.ɵfac = i03.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i03, type: _StoreDevtools, deps: [{ token: DevtoolsDispatcher }, { token: i2.ActionsSubject }, { token: i2.ReducerObservable }, { token: DevtoolsExtension }, { token: i2.ScannedActionsSubject }, { token: i03.ErrorHandler }, { token: INITIAL_STATE }, { token: STORE_DEVTOOLS_CONFIG }], target: i03.ɵɵFactoryTarget.Injectable });
782
- }
783
- static {
784
- this.ɵprov = i03.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i03, type: _StoreDevtools });
785
- }
786
- };
787
- i03.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i03, type: StoreDevtools, decorators: [{
788
- type: Injectable3
789
- }], ctorParameters: () => [{ type: DevtoolsDispatcher }, { type: i2.ActionsSubject }, { type: i2.ReducerObservable }, { type: DevtoolsExtension }, { type: i2.ScannedActionsSubject }, { type: i03.ErrorHandler }, { type: void 0, decorators: [{
790
- type: Inject2,
791
- args: [INITIAL_STATE]
792
- }] }, { type: StoreDevtoolsConfig, decorators: [{
793
- type: Inject2,
794
- args: [STORE_DEVTOOLS_CONFIG]
795
- }] }] });
796
- function emitInZone({ ngZone, connectInZone }) {
797
- return (source) => connectInZone ? new Observable2((subscriber) => source.subscribe({
798
- next: (value) => ngZone.run(() => subscriber.next(value)),
799
- error: (error) => ngZone.run(() => subscriber.error(error)),
800
- complete: () => ngZone.run(() => subscriber.complete())
801
- })) : source;
825
+ class StoreDevtools {
826
+ constructor(dispatcher, actions$, reducers$, extension, scannedActions, errorHandler, initialState, config) {
827
+ const liftedInitialState = liftInitialState(initialState, config.monitor);
828
+ const liftReducer = liftReducerWith(initialState, liftedInitialState, errorHandler, config.monitor, config);
829
+ const liftedAction$ = merge(merge(actions$.asObservable().pipe(skip(1)), extension.actions$).pipe(map(liftAction)), dispatcher, extension.liftedActions$).pipe(observeOn(queueScheduler));
830
+ const liftedReducer$ = reducers$.pipe(map(liftReducer));
831
+ const zoneConfig = injectZoneConfig(config.connectInZone);
832
+ const liftedStateSubject = new ReplaySubject(1);
833
+ this.liftedStateSubscription = liftedAction$
834
+ .pipe(withLatestFrom(liftedReducer$),
835
+ // The extension would post messages back outside of the Angular zone
836
+ // because we call `connect()` wrapped with `runOutsideAngular`. We run change
837
+ // detection only once at the end after all the required asynchronous tasks have
838
+ // been processed (for instance, `setInterval` scheduled by the `timeout` operator).
839
+ // We have to re-enter the Angular zone before the `scan` since it runs the reducer
840
+ // which must be run within the Angular zone.
841
+ emitInZone(zoneConfig), scan(({ state: liftedState }, [action, reducer]) => {
842
+ let reducedLiftedState = reducer(liftedState, action);
843
+ // On full state update
844
+ // If we have actions filters, we must filter completely our lifted state to be sync with the extension
845
+ if (action.type !== PERFORM_ACTION && shouldFilterActions(config)) {
846
+ reducedLiftedState = filterLiftedState(reducedLiftedState, config.predicate, config.actionsSafelist, config.actionsBlocklist);
847
+ }
848
+ // Extension should be sent the sanitized lifted state
849
+ extension.notify(action, reducedLiftedState);
850
+ return { state: reducedLiftedState, action };
851
+ }, { state: liftedInitialState, action: null }))
852
+ .subscribe(({ state, action }) => {
853
+ liftedStateSubject.next(state);
854
+ if (action.type === PERFORM_ACTION) {
855
+ const unliftedAction = action.action;
856
+ scannedActions.next(unliftedAction);
857
+ }
858
+ });
859
+ this.extensionStartSubscription = extension.start$
860
+ .pipe(emitInZone(zoneConfig))
861
+ .subscribe(() => {
862
+ this.refresh();
863
+ });
864
+ const liftedState$ = liftedStateSubject.asObservable();
865
+ const state$ = liftedState$.pipe(map(unliftState));
866
+ Object.defineProperty(state$, 'state', {
867
+ value: toSignal(state$, { manualCleanup: true, requireSync: true }),
868
+ });
869
+ this.dispatcher = dispatcher;
870
+ this.liftedState = liftedState$;
871
+ this.state = state$;
872
+ }
873
+ ngOnDestroy() {
874
+ // Even though the store devtools plugin is recommended to be
875
+ // used only in development mode, it can still cause a memory leak
876
+ // in microfrontend applications that are being created and destroyed
877
+ // multiple times during development. This results in excessive memory
878
+ // consumption, as it prevents entire apps from being garbage collected.
879
+ this.liftedStateSubscription.unsubscribe();
880
+ this.extensionStartSubscription.unsubscribe();
881
+ }
882
+ dispatch(action) {
883
+ this.dispatcher.next(action);
884
+ }
885
+ next(action) {
886
+ this.dispatcher.next(action);
887
+ }
888
+ error(error) { }
889
+ complete() { }
890
+ performAction(action) {
891
+ this.dispatch(new PerformAction(action, +Date.now()));
892
+ }
893
+ refresh() {
894
+ this.dispatch(new Refresh());
895
+ }
896
+ reset() {
897
+ this.dispatch(new Reset(+Date.now()));
898
+ }
899
+ rollback() {
900
+ this.dispatch(new Rollback(+Date.now()));
901
+ }
902
+ commit() {
903
+ this.dispatch(new Commit(+Date.now()));
904
+ }
905
+ sweep() {
906
+ this.dispatch(new Sweep());
907
+ }
908
+ toggleAction(id) {
909
+ this.dispatch(new ToggleAction(id));
910
+ }
911
+ jumpToAction(actionId) {
912
+ this.dispatch(new JumpToAction(actionId));
913
+ }
914
+ jumpToState(index) {
915
+ this.dispatch(new JumpToState(index));
916
+ }
917
+ importState(nextLiftedState) {
918
+ this.dispatch(new ImportState(nextLiftedState));
919
+ }
920
+ lockChanges(status) {
921
+ this.dispatch(new LockChanges(status));
922
+ }
923
+ pauseRecording(status) {
924
+ this.dispatch(new PauseRecording(status));
925
+ }
926
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtools, deps: [{ token: DevtoolsDispatcher }, { token: i2.ActionsSubject }, { token: i2.ReducerObservable }, { token: DevtoolsExtension }, { token: i2.ScannedActionsSubject }, { token: i0.ErrorHandler }, { token: INITIAL_STATE }, { token: STORE_DEVTOOLS_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); }
927
+ /** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtools }); }
928
+ }
929
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtools, decorators: [{
930
+ type: Injectable
931
+ }], ctorParameters: () => [{ type: DevtoolsDispatcher }, { type: i2.ActionsSubject }, { type: i2.ReducerObservable }, { type: DevtoolsExtension }, { type: i2.ScannedActionsSubject }, { type: i0.ErrorHandler }, { type: undefined, decorators: [{
932
+ type: Inject,
933
+ args: [INITIAL_STATE]
934
+ }] }, { type: StoreDevtoolsConfig, decorators: [{
935
+ type: Inject,
936
+ args: [STORE_DEVTOOLS_CONFIG]
937
+ }] }] });
938
+ /**
939
+ * If the devtools extension is connected out of the Angular zone,
940
+ * this operator will emit all events within the zone.
941
+ */
942
+ function emitInZone({ ngZone, connectInZone, }) {
943
+ return (source) => connectInZone
944
+ ? new Observable((subscriber) => source.subscribe({
945
+ next: (value) => ngZone.run(() => subscriber.next(value)),
946
+ error: (error) => ngZone.run(() => subscriber.error(error)),
947
+ complete: () => ngZone.run(() => subscriber.complete()),
948
+ }))
949
+ : source;
802
950
  }
803
951
 
804
- // src/provide-store-devtools.mjs
805
- import { InjectionToken as InjectionToken3, makeEnvironmentProviders } from "@angular/core";
806
- import { ReducerManagerDispatcher, StateObservable } from "@ngrx/store";
807
- var IS_EXTENSION_OR_MONITOR_PRESENT = new InjectionToken3("@ngrx/store-devtools Is Devtools Extension or Monitor Present");
952
+ const IS_EXTENSION_OR_MONITOR_PRESENT = new InjectionToken('@ngrx/store-devtools Is Devtools Extension or Monitor Present');
808
953
  function createIsExtensionOrMonitorPresent(extension, config) {
809
- return Boolean(extension) || config.monitor !== noMonitor;
954
+ return Boolean(extension) || config.monitor !== noMonitor;
810
955
  }
811
956
  function createReduxDevtoolsExtension() {
812
- const extensionKey = "__REDUX_DEVTOOLS_EXTENSION__";
813
- if (typeof window === "object" && typeof window[extensionKey] !== "undefined") {
814
- return window[extensionKey];
815
- } else {
816
- return null;
817
- }
957
+ const extensionKey = '__REDUX_DEVTOOLS_EXTENSION__';
958
+ if (typeof window === 'object' &&
959
+ typeof window[extensionKey] !== 'undefined') {
960
+ return window[extensionKey];
961
+ }
962
+ else {
963
+ return null;
964
+ }
818
965
  }
966
+ /**
967
+ * Provides developer tools and instrumentation for `Store`.
968
+ *
969
+ * @usageNotes
970
+ *
971
+ * ```ts
972
+ * bootstrapApplication(AppComponent, {
973
+ * providers: [
974
+ * provideStoreDevtools({
975
+ * maxAge: 25,
976
+ * logOnly: !isDevMode(),
977
+ * }),
978
+ * ],
979
+ * });
980
+ * ```
981
+ */
819
982
  function provideStoreDevtools(options = {}) {
820
- return makeEnvironmentProviders([
821
- DevtoolsExtension,
822
- DevtoolsDispatcher,
823
- StoreDevtools,
824
- {
825
- provide: INITIAL_OPTIONS,
826
- useValue: options
827
- },
828
- {
829
- provide: IS_EXTENSION_OR_MONITOR_PRESENT,
830
- deps: [REDUX_DEVTOOLS_EXTENSION, STORE_DEVTOOLS_CONFIG],
831
- useFactory: createIsExtensionOrMonitorPresent
832
- },
833
- {
834
- provide: REDUX_DEVTOOLS_EXTENSION,
835
- useFactory: createReduxDevtoolsExtension
836
- },
837
- {
838
- provide: STORE_DEVTOOLS_CONFIG,
839
- deps: [INITIAL_OPTIONS],
840
- useFactory: createConfig
841
- },
842
- {
843
- provide: StateObservable,
844
- deps: [StoreDevtools],
845
- useFactory: createStateObservable
846
- },
847
- {
848
- provide: ReducerManagerDispatcher,
849
- useExisting: DevtoolsDispatcher
850
- }
851
- ]);
983
+ return makeEnvironmentProviders([
984
+ DevtoolsExtension,
985
+ DevtoolsDispatcher,
986
+ StoreDevtools,
987
+ {
988
+ provide: INITIAL_OPTIONS,
989
+ useValue: options,
990
+ },
991
+ {
992
+ provide: IS_EXTENSION_OR_MONITOR_PRESENT,
993
+ deps: [REDUX_DEVTOOLS_EXTENSION, STORE_DEVTOOLS_CONFIG],
994
+ useFactory: createIsExtensionOrMonitorPresent,
995
+ },
996
+ {
997
+ provide: REDUX_DEVTOOLS_EXTENSION,
998
+ useFactory: createReduxDevtoolsExtension,
999
+ },
1000
+ {
1001
+ provide: STORE_DEVTOOLS_CONFIG,
1002
+ deps: [INITIAL_OPTIONS],
1003
+ useFactory: createConfig,
1004
+ },
1005
+ {
1006
+ provide: StateObservable,
1007
+ deps: [StoreDevtools],
1008
+ useFactory: createStateObservable,
1009
+ },
1010
+ {
1011
+ provide: ReducerManagerDispatcher,
1012
+ useExisting: DevtoolsDispatcher,
1013
+ },
1014
+ ]);
852
1015
  }
853
1016
 
854
- // src/instrument.mjs
855
- import { NgModule } from "@angular/core";
856
- import * as i04 from "@angular/core";
857
1017
  function createStateObservable(devtools) {
858
- return devtools.state;
1018
+ return devtools.state;
859
1019
  }
860
- var StoreDevtoolsModule = class _StoreDevtoolsModule {
861
- static instrument(options = {}) {
862
- return {
863
- ngModule: _StoreDevtoolsModule,
864
- providers: [provideStoreDevtools(options)]
865
- };
866
- }
867
- static {
868
- this.ɵfac = i04.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i04, type: _StoreDevtoolsModule, deps: [], target: i04.ɵɵFactoryTarget.NgModule });
869
- }
870
- static {
871
- this.ɵmod = i04.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0-next.6", ngImport: i04, type: _StoreDevtoolsModule });
872
- }
873
- static {
874
- this.ɵinj = i04.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i04, type: _StoreDevtoolsModule });
875
- }
876
- };
877
- i04.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0-next.6", ngImport: i04, type: StoreDevtoolsModule, decorators: [{
878
- type: NgModule,
879
- args: [{}]
880
- }] });
881
- export {
882
- INITIAL_OPTIONS,
883
- RECOMPUTE,
884
- REDUX_DEVTOOLS_EXTENSION,
885
- StoreDevtools,
886
- StoreDevtoolsConfig,
887
- StoreDevtoolsModule,
888
- provideStoreDevtools
889
- };
1020
+ class StoreDevtoolsModule {
1021
+ static instrument(options = {}) {
1022
+ return {
1023
+ ngModule: StoreDevtoolsModule,
1024
+ providers: [provideStoreDevtools(options)],
1025
+ };
1026
+ }
1027
+ /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtoolsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1028
+ /** @nocollapse */ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtoolsModule }); }
1029
+ /** @nocollapse */ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtoolsModule }); }
1030
+ }
1031
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.0.0", ngImport: i0, type: StoreDevtoolsModule, decorators: [{
1032
+ type: NgModule,
1033
+ args: [{}]
1034
+ }] });
1035
+
1036
+ /**
1037
+ * DO NOT EDIT
1038
+ *
1039
+ * This file is automatically generated at build
1040
+ */
1041
+
1042
+ /**
1043
+ * Generated bundle index. Do not edit.
1044
+ */
1045
+
1046
+ export { INITIAL_OPTIONS, RECOMPUTE, REDUX_DEVTOOLS_EXTENSION, StoreDevtools, StoreDevtoolsConfig, StoreDevtoolsModule, provideStoreDevtools };
890
1047
  //# sourceMappingURL=ngrx-store-devtools.mjs.map