@ngrx/store-devtools 18.0.0-beta.0 → 18.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm2022/src/devtools-dispatcher.mjs +4 -4
- package/esm2022/src/devtools.mjs +4 -4
- package/esm2022/src/extension.mjs +4 -4
- package/esm2022/src/instrument.mjs +5 -5
- package/fesm2022/ngrx-store-devtools.mjs +980 -823
- package/fesm2022/ngrx-store-devtools.mjs.map +1 -7
- package/package.json +2 -2
- package/schematics-core/utility/libs-version.js +1 -1
- package/schematics-core/utility/libs-version.js.map +1 -1
|
@@ -1,890 +1,1047 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
126
|
+
return null;
|
|
109
127
|
}
|
|
110
|
-
|
|
128
|
+
const DEFAULT_NAME = 'NgRx Store DevTools';
|
|
111
129
|
function createConfig(optionsInput) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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
|
-
|
|
242
|
-
|
|
288
|
+
const ngZone = connectInZone ? inject(NgZone) : null;
|
|
289
|
+
return { ngZone, connectInZone };
|
|
243
290
|
}
|
|
244
291
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
|
|
400
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
const
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
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
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
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
|
-
|
|
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
|
-
|
|
954
|
+
return Boolean(extension) || config.monitor !== noMonitor;
|
|
810
955
|
}
|
|
811
956
|
function createReduxDevtoolsExtension() {
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
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
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
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
|
-
|
|
1018
|
+
return devtools.state;
|
|
859
1019
|
}
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
this.ɵ
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
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
|