@redux-devtools/instrument
Advanced tools
| export default function getSymbolObservable(): typeof Symbol.observable | "@@observable"; |
| export default function getSymbolObservable() { | ||
| return (typeof Symbol === 'function' && Symbol.observable) || '@@observable'; | ||
| } |
| import { Action, Reducer, Store, StoreEnhancer } from 'redux'; | ||
| export declare const ActionTypes: { | ||
| readonly PERFORM_ACTION: "PERFORM_ACTION"; | ||
| readonly RESET: "RESET"; | ||
| readonly ROLLBACK: "ROLLBACK"; | ||
| readonly COMMIT: "COMMIT"; | ||
| readonly SWEEP: "SWEEP"; | ||
| readonly TOGGLE_ACTION: "TOGGLE_ACTION"; | ||
| readonly SET_ACTIONS_ACTIVE: "SET_ACTIONS_ACTIVE"; | ||
| readonly JUMP_TO_STATE: "JUMP_TO_STATE"; | ||
| readonly JUMP_TO_ACTION: "JUMP_TO_ACTION"; | ||
| readonly REORDER_ACTION: "REORDER_ACTION"; | ||
| readonly IMPORT_STATE: "IMPORT_STATE"; | ||
| readonly LOCK_CHANGES: "LOCK_CHANGES"; | ||
| readonly PAUSE_RECORDING: "PAUSE_RECORDING"; | ||
| }; | ||
| export interface PerformAction<A extends Action<string>> { | ||
| type: typeof ActionTypes.PERFORM_ACTION; | ||
| action: A; | ||
| timestamp: number; | ||
| stack: string | undefined; | ||
| } | ||
| interface ResetAction { | ||
| type: typeof ActionTypes.RESET; | ||
| timestamp: number; | ||
| } | ||
| interface RollbackAction { | ||
| type: typeof ActionTypes.ROLLBACK; | ||
| timestamp: number; | ||
| } | ||
| interface CommitAction { | ||
| type: typeof ActionTypes.COMMIT; | ||
| timestamp: number; | ||
| } | ||
| interface SweepAction { | ||
| type: typeof ActionTypes.SWEEP; | ||
| } | ||
| interface ToggleAction { | ||
| type: typeof ActionTypes.TOGGLE_ACTION; | ||
| id: number; | ||
| } | ||
| interface SetActionsActiveAction { | ||
| type: typeof ActionTypes.SET_ACTIONS_ACTIVE; | ||
| start: number; | ||
| end: number; | ||
| active: boolean; | ||
| } | ||
| interface ReorderAction { | ||
| type: typeof ActionTypes.REORDER_ACTION; | ||
| actionId: number; | ||
| beforeActionId: number; | ||
| } | ||
| interface JumpToStateAction { | ||
| type: typeof ActionTypes.JUMP_TO_STATE; | ||
| index: number; | ||
| } | ||
| interface JumpToActionAction { | ||
| type: typeof ActionTypes.JUMP_TO_ACTION; | ||
| actionId: number; | ||
| } | ||
| interface ImportStateAction<S, A extends Action<string>, MonitorState> { | ||
| type: typeof ActionTypes.IMPORT_STATE; | ||
| nextLiftedState: LiftedState<S, A, MonitorState> | readonly A[]; | ||
| preloadedState?: S; | ||
| noRecompute?: boolean | undefined; | ||
| } | ||
| interface LockChangesAction { | ||
| type: typeof ActionTypes.LOCK_CHANGES; | ||
| status: boolean; | ||
| } | ||
| interface PauseRecordingAction { | ||
| type: typeof ActionTypes.PAUSE_RECORDING; | ||
| status: boolean; | ||
| } | ||
| export type LiftedAction<S, A extends Action<string>, MonitorState> = PerformAction<A> | ResetAction | RollbackAction | CommitAction | SweepAction | ToggleAction | SetActionsActiveAction | ReorderAction | JumpToStateAction | JumpToActionAction | ImportStateAction<S, A, MonitorState> | LockChangesAction | PauseRecordingAction; | ||
| /** | ||
| * Action creators to change the History state. | ||
| */ | ||
| export declare const ActionCreators: { | ||
| performAction<A extends Action<string>>(this: void, action: A, trace?: ((action: A) => string | undefined) | boolean, traceLimit?: number, toExcludeFromTrace?: Function): { | ||
| type: "PERFORM_ACTION"; | ||
| action: A; | ||
| timestamp: number; | ||
| stack: string | undefined; | ||
| }; | ||
| reset(this: void): ResetAction; | ||
| rollback(this: void): RollbackAction; | ||
| commit(this: void): CommitAction; | ||
| sweep(this: void): SweepAction; | ||
| toggleAction(this: void, id: number): ToggleAction; | ||
| setActionsActive(this: void, start: number, end: number, active?: boolean): SetActionsActiveAction; | ||
| reorderAction(this: void, actionId: number, beforeActionId: number): ReorderAction; | ||
| jumpToState(this: void, index: number): JumpToStateAction; | ||
| jumpToAction(this: void, actionId: number): JumpToActionAction; | ||
| importState<S, A extends Action<string>, MonitorState = null>(this: void, nextLiftedState: LiftedState<S, A, MonitorState> | readonly A[], noRecompute?: boolean): ImportStateAction<S, A, MonitorState>; | ||
| lockChanges(this: void, status: boolean): LockChangesAction; | ||
| pauseRecording(this: void, status: boolean): PauseRecordingAction; | ||
| }; | ||
| export declare const INIT_ACTION: { | ||
| type: string; | ||
| }; | ||
| export interface LiftedState<S, A extends Action<string>, MonitorState> { | ||
| monitorState: MonitorState; | ||
| nextActionId: number; | ||
| actionsById: { | ||
| [actionId: number]: PerformAction<A>; | ||
| }; | ||
| stagedActionIds: number[]; | ||
| skippedActionIds: number[]; | ||
| committedState: S; | ||
| currentStateIndex: number; | ||
| computedStates: { | ||
| state: S; | ||
| error?: string; | ||
| }[]; | ||
| isLocked: boolean; | ||
| isPaused: boolean; | ||
| } | ||
| export type LiftedReducer<S, A extends Action<string>, MonitorState> = Reducer<LiftedState<S, A, MonitorState>, LiftedAction<S, A, MonitorState>>; | ||
| export type LiftedStore<S, A extends Action<string>, MonitorState> = Store<LiftedState<S, A, MonitorState>, LiftedAction<S, A, MonitorState>>; | ||
| export type InstrumentExt<S, A extends Action<string>, MonitorState> = { | ||
| liftedStore: LiftedStore<S, A, MonitorState>; | ||
| }; | ||
| export type EnhancedStore<S, A extends Action<string>, MonitorState> = Store<S, A> & InstrumentExt<S, A, MonitorState>; | ||
| export interface Options<S, A extends Action<string>, MonitorState, MonitorAction extends Action<string>> { | ||
| maxAge?: number | ((currentLiftedAction: LiftedAction<S, A, MonitorState>, previousLiftedState: LiftedState<S, A, MonitorState> | undefined) => number); | ||
| shouldCatchErrors?: boolean; | ||
| shouldRecordChanges?: boolean; | ||
| pauseActionType?: unknown; | ||
| shouldStartLocked?: boolean; | ||
| shouldHotReload?: boolean; | ||
| trace?: boolean | ((action: A) => string | undefined); | ||
| traceLimit?: number; | ||
| shouldIncludeCallstack?: boolean; | ||
| } | ||
| /** | ||
| * Redux instrumentation store enhancer. | ||
| */ | ||
| export declare function instrument<OptionsS, OptionsA extends Action<string>, MonitorState = null, MonitorAction extends Action<string> = never>(monitorReducer?: Reducer<MonitorState, MonitorAction>, options?: Options<OptionsS, OptionsA, MonitorState, MonitorAction>): StoreEnhancer<InstrumentExt<any, any, MonitorState>>; | ||
| export {}; |
| import { isPlainObject, } from 'redux'; | ||
| import getSymbolObservable from './getSymbolObservable.js'; | ||
| export const ActionTypes = { | ||
| PERFORM_ACTION: 'PERFORM_ACTION', | ||
| RESET: 'RESET', | ||
| ROLLBACK: 'ROLLBACK', | ||
| COMMIT: 'COMMIT', | ||
| SWEEP: 'SWEEP', | ||
| TOGGLE_ACTION: 'TOGGLE_ACTION', | ||
| SET_ACTIONS_ACTIVE: 'SET_ACTIONS_ACTIVE', | ||
| JUMP_TO_STATE: 'JUMP_TO_STATE', | ||
| JUMP_TO_ACTION: 'JUMP_TO_ACTION', | ||
| REORDER_ACTION: 'REORDER_ACTION', | ||
| IMPORT_STATE: 'IMPORT_STATE', | ||
| LOCK_CHANGES: 'LOCK_CHANGES', | ||
| PAUSE_RECORDING: 'PAUSE_RECORDING', | ||
| }; | ||
| const isChrome = typeof window === 'object' && | ||
| (typeof window.chrome !== | ||
| 'undefined' || | ||
| (typeof window.process !== 'undefined' && | ||
| window.process.type === | ||
| 'renderer')); | ||
| const isChromeOrNode = isChrome || | ||
| (typeof process !== 'undefined' && | ||
| process.release && | ||
| process.release.name === 'node'); | ||
| /** | ||
| * Action creators to change the History state. | ||
| */ | ||
| export const ActionCreators = { | ||
| performAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type | ||
| toExcludeFromTrace) { | ||
| if (!isPlainObject(action)) { | ||
| throw new Error('Actions must be plain objects. ' + | ||
| 'Use custom middleware for async actions.'); | ||
| } | ||
| if (typeof action.type === 'undefined') { | ||
| throw new Error('Actions may not have an undefined "type" property. ' + | ||
| 'Have you misspelled a constant?'); | ||
| } | ||
| let stack; | ||
| if (trace) { | ||
| let extraFrames = 0; | ||
| if (typeof trace === 'function') { | ||
| stack = trace(action); | ||
| } | ||
| else { | ||
| const error = Error(); | ||
| let prevStackTraceLimit; | ||
| if (Error.captureStackTrace && isChromeOrNode) { | ||
| // avoid error-polyfill | ||
| if (traceLimit && Error.stackTraceLimit < traceLimit) { | ||
| prevStackTraceLimit = Error.stackTraceLimit; | ||
| Error.stackTraceLimit = traceLimit; | ||
| } | ||
| Error.captureStackTrace(error, toExcludeFromTrace); | ||
| } | ||
| else { | ||
| extraFrames = 3; | ||
| } | ||
| stack = error.stack; | ||
| if (prevStackTraceLimit) | ||
| Error.stackTraceLimit = prevStackTraceLimit; | ||
| if (extraFrames || | ||
| typeof Error.stackTraceLimit !== 'number' || | ||
| (traceLimit && Error.stackTraceLimit > traceLimit)) { | ||
| if (stack != null) { | ||
| const frames = stack.split('\n'); | ||
| if (traceLimit && frames.length > traceLimit) { | ||
| stack = frames | ||
| .slice(0, traceLimit + | ||
| extraFrames + | ||
| (frames[0].startsWith('Error') ? 1 : 0)) | ||
| .join('\n'); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| type: ActionTypes.PERFORM_ACTION, | ||
| action, | ||
| timestamp: Date.now(), | ||
| stack, | ||
| }; | ||
| }, | ||
| reset() { | ||
| return { type: ActionTypes.RESET, timestamp: Date.now() }; | ||
| }, | ||
| rollback() { | ||
| return { type: ActionTypes.ROLLBACK, timestamp: Date.now() }; | ||
| }, | ||
| commit() { | ||
| return { type: ActionTypes.COMMIT, timestamp: Date.now() }; | ||
| }, | ||
| sweep() { | ||
| return { type: ActionTypes.SWEEP }; | ||
| }, | ||
| toggleAction(id) { | ||
| return { type: ActionTypes.TOGGLE_ACTION, id }; | ||
| }, | ||
| setActionsActive(start, end, active = true) { | ||
| return { type: ActionTypes.SET_ACTIONS_ACTIVE, start, end, active }; | ||
| }, | ||
| reorderAction(actionId, beforeActionId) { | ||
| return { type: ActionTypes.REORDER_ACTION, actionId, beforeActionId }; | ||
| }, | ||
| jumpToState(index) { | ||
| return { type: ActionTypes.JUMP_TO_STATE, index }; | ||
| }, | ||
| jumpToAction(actionId) { | ||
| return { type: ActionTypes.JUMP_TO_ACTION, actionId }; | ||
| }, | ||
| importState(nextLiftedState, noRecompute) { | ||
| return { type: ActionTypes.IMPORT_STATE, nextLiftedState, noRecompute }; | ||
| }, | ||
| lockChanges(status) { | ||
| return { type: ActionTypes.LOCK_CHANGES, status }; | ||
| }, | ||
| pauseRecording(status) { | ||
| return { type: ActionTypes.PAUSE_RECORDING, status }; | ||
| }, | ||
| }; | ||
| export const INIT_ACTION = { type: '@@INIT' }; | ||
| /** | ||
| * Computes the next entry with exceptions catching. | ||
| */ | ||
| function computeWithTryCatch(reducer, action, state) { | ||
| let nextState = state; | ||
| let nextError; | ||
| try { | ||
| nextState = reducer(state, action); | ||
| } | ||
| catch (err) { | ||
| nextError = err.toString(); | ||
| if (isChrome) { | ||
| // In Chrome, rethrowing provides better source map support | ||
| setTimeout(() => { | ||
| throw err; | ||
| }); | ||
| } | ||
| else { | ||
| console.error(err); // eslint-disable-line no-console | ||
| } | ||
| } | ||
| return { | ||
| state: nextState, | ||
| error: nextError, | ||
| }; | ||
| } | ||
| /** | ||
| * Computes the next entry in the log by applying an action. | ||
| */ | ||
| function computeNextEntry(reducer, action, state, shouldCatchErrors) { | ||
| if (!shouldCatchErrors) { | ||
| return { state: reducer(state, action) }; | ||
| } | ||
| return computeWithTryCatch(reducer, action, state); | ||
| } | ||
| /** | ||
| * Runs the reducer on invalidated actions to get a fresh computation log. | ||
| */ | ||
| function recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, shouldCatchErrors) { | ||
| // Optimization: exit early and return the same reference | ||
| // if we know nothing could have changed. | ||
| if (!computedStates || | ||
| minInvalidatedStateIndex === -1 || | ||
| (minInvalidatedStateIndex >= computedStates.length && | ||
| computedStates.length === stagedActionIds.length)) { | ||
| return computedStates; | ||
| } | ||
| const nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex); | ||
| for (let i = minInvalidatedStateIndex; i < stagedActionIds.length; i++) { | ||
| const actionId = stagedActionIds[i]; | ||
| const action = actionsById[actionId].action; | ||
| const previousEntry = nextComputedStates[i - 1]; | ||
| const previousState = previousEntry ? previousEntry.state : committedState; | ||
| const shouldSkip = skippedActionIds.includes(actionId); | ||
| let entry; | ||
| if (shouldSkip) { | ||
| entry = previousEntry; | ||
| } | ||
| else { | ||
| if (shouldCatchErrors && previousEntry && previousEntry.error) { | ||
| entry = { | ||
| state: previousState, | ||
| error: 'Interrupted by an error up the chain', | ||
| }; | ||
| } | ||
| else { | ||
| entry = computeNextEntry(reducer, action, previousState, shouldCatchErrors); | ||
| } | ||
| } | ||
| nextComputedStates.push(entry); | ||
| } | ||
| return nextComputedStates; | ||
| } | ||
| /** | ||
| * Lifts an app's action into an action on the lifted store. | ||
| */ | ||
| function liftAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type | ||
| toExcludeFromTrace) { | ||
| return ActionCreators.performAction(action, trace, traceLimit, toExcludeFromTrace); | ||
| } | ||
| function isArray(nextLiftedState) { | ||
| return Array.isArray(nextLiftedState); | ||
| } | ||
| /** | ||
| * Creates a history state reducer from an app's reducer. | ||
| */ | ||
| function liftReducerWith(reducer, initialCommittedState, monitorReducer, options) { | ||
| const initialLiftedState = { | ||
| monitorState: monitorReducer(undefined, {}), | ||
| nextActionId: 1, | ||
| actionsById: { 0: liftAction(INIT_ACTION) }, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: initialCommittedState, | ||
| currentStateIndex: 0, | ||
| computedStates: [], | ||
| isLocked: options.shouldStartLocked === true, | ||
| isPaused: options.shouldRecordChanges === false, | ||
| }; | ||
| /** | ||
| * Manages how the history actions modify the history state. | ||
| */ | ||
| return (liftedState, liftedAction) => { | ||
| let { monitorState, actionsById, nextActionId, stagedActionIds, skippedActionIds, committedState, currentStateIndex, computedStates, isLocked, isPaused, } = liftedState || initialLiftedState; | ||
| if (!liftedState) { | ||
| // Prevent mutating initialLiftedState | ||
| actionsById = { ...actionsById }; | ||
| } | ||
| function commitExcessActions(n) { | ||
| // Auto-commits n-number of excess actions. | ||
| let excess = n; | ||
| let idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| for (let i = 0; i < idsToDelete.length; i++) { | ||
| if (computedStates[i + 1].error) { | ||
| // Stop if error is found. Commit actions up to error. | ||
| excess = i; | ||
| idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| break; | ||
| } | ||
| else { | ||
| delete actionsById[idsToDelete[i]]; | ||
| } | ||
| } | ||
| skippedActionIds = skippedActionIds.filter((id) => !idsToDelete.includes(id)); | ||
| stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)]; | ||
| committedState = computedStates[excess].state; | ||
| computedStates = computedStates.slice(excess); | ||
| currentStateIndex = | ||
| currentStateIndex > excess ? currentStateIndex - excess : 0; | ||
| } | ||
| function computePausedAction(shouldInit) { | ||
| let computedState; | ||
| if (shouldInit) { | ||
| computedState = computedStates[currentStateIndex]; | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| } | ||
| else { | ||
| computedState = computeNextEntry(reducer, liftedAction.action, computedStates[currentStateIndex].state, false); | ||
| } | ||
| if (!options.pauseActionType || nextActionId === 1) { | ||
| return { | ||
| monitorState, | ||
| actionsById: { 0: liftAction(INIT_ACTION) }, | ||
| nextActionId: 1, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: computedState.state, | ||
| currentStateIndex: 0, | ||
| computedStates: [computedState], | ||
| isLocked, | ||
| isPaused: true, | ||
| }; | ||
| } | ||
| if (shouldInit) { | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| stagedActionIds = [...stagedActionIds, nextActionId]; | ||
| nextActionId++; | ||
| } | ||
| return { | ||
| monitorState, | ||
| actionsById: { | ||
| ...actionsById, | ||
| [nextActionId - 1]: liftAction({ | ||
| type: options.pauseActionType, | ||
| }), | ||
| }, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates: [ | ||
| ...computedStates.slice(0, stagedActionIds.length - 1), | ||
| computedState, | ||
| ], | ||
| isLocked, | ||
| isPaused: true, | ||
| }; | ||
| } | ||
| // By default, aggressively recompute every state whatever happens. | ||
| // This has O(n) performance, so we'll override this to a sensible | ||
| // value whenever we feel like we don't have to recompute the states. | ||
| let minInvalidatedStateIndex = 0; | ||
| // maxAge number can be changed dynamically | ||
| let maxAge = options.maxAge; | ||
| if (typeof maxAge === 'function') | ||
| maxAge = maxAge(liftedAction, liftedState); | ||
| if (/^@@redux\/(INIT|REPLACE)/.test(liftedAction.type)) { | ||
| if (options.shouldHotReload === false) { | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = | ||
| computedStates.length === 0 | ||
| ? initialCommittedState | ||
| : computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| } | ||
| // Recompute states on hot reload and init. | ||
| minInvalidatedStateIndex = 0; | ||
| if (maxAge && stagedActionIds.length > maxAge) { | ||
| // States must be recomputed before committing excess. | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| commitExcessActions(stagedActionIds.length - maxAge); | ||
| // Avoid double computation. | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } | ||
| else { | ||
| switch (liftedAction.type) { | ||
| case ActionTypes.PERFORM_ACTION: { | ||
| if (isLocked) | ||
| return liftedState || initialLiftedState; | ||
| if (isPaused) | ||
| return computePausedAction(); | ||
| // Auto-commit as new actions come in. | ||
| if (maxAge && stagedActionIds.length >= maxAge) { | ||
| commitExcessActions(stagedActionIds.length - maxAge + 1); | ||
| } | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| const actionId = nextActionId++; | ||
| // Mutation! This is the hottest path, and we optimize on purpose. | ||
| // It is safe because we set a new key in a cache dictionary. | ||
| actionsById[actionId] = liftedAction; | ||
| stagedActionIds = [...stagedActionIds, actionId]; | ||
| // Optimization: we know that only the new action needs computing. | ||
| minInvalidatedStateIndex = stagedActionIds.length - 1; | ||
| break; | ||
| } | ||
| case ActionTypes.RESET: { | ||
| // Get back to the state the store was created with. | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = initialCommittedState; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.COMMIT: { | ||
| // Consider the last committed state the new starting point. | ||
| // Squash any staged actions into a single committed state. | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.ROLLBACK: { | ||
| // Forget about any staged actions. | ||
| // Start again from the last committed state. | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.TOGGLE_ACTION: { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { id: actionId } = liftedAction; | ||
| const index = skippedActionIds.indexOf(actionId); | ||
| if (index === -1) { | ||
| skippedActionIds = [actionId, ...skippedActionIds]; | ||
| } | ||
| else { | ||
| skippedActionIds = skippedActionIds.filter((id) => id !== actionId); | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(actionId); | ||
| break; | ||
| } | ||
| case ActionTypes.SET_ACTIONS_ACTIVE: { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { start, end, active } = liftedAction; | ||
| const actionIds = []; | ||
| for (let i = start; i < end; i++) | ||
| actionIds.push(i); | ||
| if (active) { | ||
| const actionIdsSet = new Set(actionIds); | ||
| skippedActionIds = skippedActionIds.filter((actionId) => !actionIdsSet.has(actionId)); | ||
| } | ||
| else { | ||
| const skippedActionIdsSet = new Set(skippedActionIds); | ||
| skippedActionIds = [ | ||
| ...skippedActionIds, | ||
| ...actionIds.filter((actionId) => !skippedActionIdsSet.has(actionId)), | ||
| ]; | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(start); | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_STATE: { | ||
| // Without recomputing anything, move the pointer that tell us | ||
| // which state is considered the current one. Useful for sliders. | ||
| currentStateIndex = liftedAction.index; | ||
| // Optimization: we know the history has not changed. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_ACTION: { | ||
| // Jumps to a corresponding state to a specific action. | ||
| // Useful when filtering actions. | ||
| const index = stagedActionIds.indexOf(liftedAction.actionId); | ||
| if (index !== -1) | ||
| currentStateIndex = index; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.SWEEP: { | ||
| // Forget any actions that are currently being skipped. | ||
| const skippedActionIdsSet = new Set(skippedActionIds); | ||
| stagedActionIds = stagedActionIds.filter((actionId) => !skippedActionIdsSet.has(actionId)); | ||
| skippedActionIds = []; | ||
| currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1); | ||
| break; | ||
| } | ||
| case ActionTypes.REORDER_ACTION: { | ||
| // Recompute actions in a new order. | ||
| const actionId = liftedAction.actionId; | ||
| const idx = stagedActionIds.indexOf(actionId); | ||
| // do nothing in case the action is already removed or trying to move the first action | ||
| if (idx < 1) | ||
| break; | ||
| const beforeActionId = liftedAction.beforeActionId; | ||
| let newIdx = stagedActionIds.indexOf(beforeActionId); | ||
| if (newIdx < 1) { | ||
| // move to the beginning or to the end | ||
| const count = stagedActionIds.length; | ||
| newIdx = beforeActionId > stagedActionIds[count - 1] ? count : 1; | ||
| } | ||
| const diff = idx - newIdx; | ||
| if (diff > 0) { | ||
| // move left | ||
| stagedActionIds = [ | ||
| ...stagedActionIds.slice(0, newIdx), | ||
| actionId, | ||
| ...stagedActionIds.slice(newIdx, idx), | ||
| ...stagedActionIds.slice(idx + 1), | ||
| ]; | ||
| minInvalidatedStateIndex = newIdx; | ||
| } | ||
| else if (diff < 0) { | ||
| // move right | ||
| stagedActionIds = [ | ||
| ...stagedActionIds.slice(0, idx), | ||
| ...stagedActionIds.slice(idx + 1, newIdx), | ||
| actionId, | ||
| ...stagedActionIds.slice(newIdx), | ||
| ]; | ||
| minInvalidatedStateIndex = idx; | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.IMPORT_STATE: { | ||
| if (isArray(liftedAction.nextLiftedState)) { | ||
| // recompute array of actions | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = liftedAction.nextLiftedState.length; | ||
| computedStates = []; | ||
| committedState = liftedAction.preloadedState; | ||
| minInvalidatedStateIndex = 0; | ||
| // iterate through actions | ||
| liftedAction.nextLiftedState.forEach((action) => { | ||
| actionsById[nextActionId] = liftAction(action, options.trace || options.shouldIncludeCallstack); | ||
| stagedActionIds.push(nextActionId); | ||
| nextActionId++; | ||
| }); | ||
| } | ||
| else { | ||
| // Completely replace everything. | ||
| ({ | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| } = liftedAction.nextLiftedState); | ||
| if (liftedAction.noRecompute) { | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.LOCK_CHANGES: { | ||
| isLocked = liftedAction.status; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.PAUSE_RECORDING: { | ||
| isPaused = liftedAction.status; | ||
| if (isPaused) { | ||
| return computePausedAction(true); | ||
| } | ||
| // Commit when unpausing | ||
| actionsById = { 0: liftAction(INIT_ACTION) }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| default: { | ||
| // If the action is not recognized, it's a monitor action. | ||
| // Optimization: a monitor action can't change history. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| return { | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| isLocked, | ||
| isPaused, | ||
| }; | ||
| }; | ||
| } | ||
| /** | ||
| * Provides an app's view into the state of the lifted store. | ||
| */ | ||
| function unliftState(liftedState) { | ||
| const { computedStates, currentStateIndex } = liftedState; | ||
| const { state } = computedStates[currentStateIndex]; | ||
| return state; | ||
| } | ||
| /** | ||
| * Provides an app's view into the lifted store. | ||
| */ | ||
| function unliftStore(liftedStore, liftReducer, options) { | ||
| let lastDefinedState; | ||
| const trace = options.trace || options.shouldIncludeCallstack; | ||
| const traceLimit = options.traceLimit || 10; | ||
| function getState() { | ||
| const state = unliftState(liftedStore.getState()); | ||
| if (state !== undefined) { | ||
| lastDefinedState = state; | ||
| } | ||
| return lastDefinedState; | ||
| } | ||
| function dispatch(action) { | ||
| liftedStore.dispatch(liftAction(action, trace, traceLimit, dispatch)); | ||
| return action; | ||
| } | ||
| const $$observable = getSymbolObservable(); | ||
| if (!($$observable in liftedStore)) { | ||
| console.warn('Symbol.observable as defined by Redux and Redux DevTools do not match. This could cause your app to behave differently if the DevTools are not loaded. Consider polyfilling Symbol.observable before Redux is imported or avoid polyfilling Symbol.observable altogether.'); | ||
| } | ||
| return { | ||
| liftedStore, | ||
| dispatch, | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| subscribe: liftedStore.subscribe, | ||
| getState, | ||
| replaceReducer(nextReducer) { | ||
| liftedStore.replaceReducer(liftReducer(nextReducer)); | ||
| }, | ||
| [$$observable]() { | ||
| return { | ||
| subscribe(observer) { | ||
| if (typeof observer !== 'object') { | ||
| throw new TypeError('Expected the observer to be an object.'); | ||
| } | ||
| function observeState() { | ||
| if (observer.next) { | ||
| observer.next(getState()); | ||
| } | ||
| } | ||
| observeState(); | ||
| const unsubscribe = liftedStore.subscribe(observeState); | ||
| return { unsubscribe }; | ||
| }, | ||
| [$$observable]() { | ||
| return this; | ||
| }, | ||
| }; | ||
| }, | ||
| }; | ||
| } | ||
| /** | ||
| * Redux instrumentation store enhancer. | ||
| */ | ||
| export function instrument(monitorReducer = (() => null), options = {}) { | ||
| if (typeof options.maxAge === 'number' && options.maxAge < 2) { | ||
| throw new Error('DevTools.instrument({ maxAge }) option, if specified, ' + | ||
| 'may not be less than 2.'); | ||
| } | ||
| return (createStore) => (reducer, initialState) => { | ||
| function liftReducer(r) { | ||
| if (typeof r !== 'function') { | ||
| if (r && typeof r.default === 'function') { | ||
| throw new Error('Expected the reducer to be a function. ' + | ||
| 'Instead got an object with a "default" field. ' + | ||
| 'Did you pass a module instead of the default export? ' + | ||
| 'Try passing require(...).default instead.'); | ||
| } | ||
| throw new Error('Expected the reducer to be a function.'); | ||
| } | ||
| return liftReducerWith(r, initialState, monitorReducer, options); | ||
| } | ||
| const liftedStore = createStore(liftReducer(reducer)); | ||
| if (liftedStore.liftedStore) { | ||
| throw new Error('DevTools instrumentation should not be applied more than once. ' + | ||
| 'Check your store configuration.'); | ||
| } | ||
| return unliftStore(liftedStore, liftReducer, options); | ||
| }; | ||
| } |
+15
-34
| { | ||
| "name": "@redux-devtools/instrument", | ||
| "version": "2.2.0", | ||
| "version": "3.0.0", | ||
| "description": "Redux DevTools instrumentation", | ||
@@ -23,5 +23,4 @@ "keywords": [ | ||
| ], | ||
| "main": "lib/cjs/instrument.js", | ||
| "module": "lib/esm/instrument.js", | ||
| "types": "lib/types/instrument.d.ts", | ||
| "exports": "./lib/instrument.js", | ||
| "type": "module", | ||
| "sideEffects": false, | ||
@@ -32,27 +31,12 @@ "repository": { | ||
| }, | ||
| "dependencies": { | ||
| "@babel/runtime": "^7.23.2", | ||
| "lodash": "^4.17.21" | ||
| }, | ||
| "devDependencies": { | ||
| "@babel/cli": "^7.23.0", | ||
| "@babel/core": "^7.23.3", | ||
| "@babel/eslint-parser": "^7.23.3", | ||
| "@babel/plugin-transform-runtime": "^7.23.3", | ||
| "@babel/preset-env": "^7.23.3", | ||
| "@babel/preset-typescript": "^7.23.3", | ||
| "@types/jest": "^29.5.8", | ||
| "@types/lodash": "^4.14.201", | ||
| "@types/node": "^20.9.0", | ||
| "@typescript-eslint/eslint-plugin": "^6.11.0", | ||
| "@typescript-eslint/parser": "^6.11.0", | ||
| "eslint": "^8.53.0", | ||
| "eslint-config-prettier": "^9.0.0", | ||
| "eslint-plugin-jest": "^27.6.0", | ||
| "jest": "^29.7.0", | ||
| "redux": "^4.2.1", | ||
| "rimraf": "^5.0.5", | ||
| "rxjs": "^7.8.1", | ||
| "ts-jest": "^29.1.1", | ||
| "typescript": "~5.2.2" | ||
| "@jest/globals": "^30.2.0", | ||
| "@types/jest": "^30.0.0", | ||
| "@types/node": "^24.11.0", | ||
| "jest": "^30.2.0", | ||
| "redux": "^5.0.1", | ||
| "rimraf": "^6.1.3", | ||
| "rxjs": "^7.8.2", | ||
| "ts-jest": "^29.4.6", | ||
| "typescript": "~5.9.3" | ||
| }, | ||
@@ -63,9 +47,6 @@ "peerDependencies": { | ||
| "scripts": { | ||
| "build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types", | ||
| "build:cjs": "babel src --extensions \".ts\" --out-dir lib/cjs", | ||
| "build:esm": "babel src --config-file ./babel.config.esm.json --extensions \".ts\" --out-dir lib/esm", | ||
| "build:types": "tsc --emitDeclarationOnly", | ||
| "build": "tsc", | ||
| "clean": "rimraf lib", | ||
| "test": "jest", | ||
| "lint": "eslint . --ext .ts", | ||
| "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", | ||
| "lint": "eslint .", | ||
| "type-check": "tsc --noEmit", | ||
@@ -72,0 +53,0 @@ "prepublish": "pnpm run type-check && pnpm run lint && pnpm run test" |
+74
-41
@@ -1,8 +0,5 @@ | ||
| import difference from 'lodash/difference'; | ||
| import union from 'lodash/union'; | ||
| import isPlainObject from 'lodash/isPlainObject'; | ||
| import { | ||
| Action, | ||
| isPlainObject, | ||
| Observer, | ||
| PreloadedState, | ||
| Reducer, | ||
@@ -13,3 +10,3 @@ Store, | ||
| } from 'redux'; | ||
| import getSymbolObservable from './getSymbolObservable'; | ||
| import getSymbolObservable from './getSymbolObservable.js'; | ||
@@ -137,6 +134,7 @@ export const ActionTypes = { | ||
| performAction<A extends Action<string>>( | ||
| this: void, | ||
| action: A, | ||
| trace?: ((action: A) => string | undefined) | boolean, | ||
| traceLimit?: number, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type | ||
| toExcludeFromTrace?: Function, | ||
@@ -208,19 +206,19 @@ ) { | ||
| reset(): ResetAction { | ||
| reset(this: void): ResetAction { | ||
| return { type: ActionTypes.RESET, timestamp: Date.now() }; | ||
| }, | ||
| rollback(): RollbackAction { | ||
| rollback(this: void): RollbackAction { | ||
| return { type: ActionTypes.ROLLBACK, timestamp: Date.now() }; | ||
| }, | ||
| commit(): CommitAction { | ||
| commit(this: void): CommitAction { | ||
| return { type: ActionTypes.COMMIT, timestamp: Date.now() }; | ||
| }, | ||
| sweep(): SweepAction { | ||
| sweep(this: void): SweepAction { | ||
| return { type: ActionTypes.SWEEP }; | ||
| }, | ||
| toggleAction(id: number): ToggleAction { | ||
| toggleAction(this: void, id: number): ToggleAction { | ||
| return { type: ActionTypes.TOGGLE_ACTION, id }; | ||
@@ -230,2 +228,3 @@ }, | ||
| setActionsActive( | ||
| this: void, | ||
| start: number, | ||
@@ -238,11 +237,15 @@ end: number, | ||
| reorderAction(actionId: number, beforeActionId: number): ReorderAction { | ||
| reorderAction( | ||
| this: void, | ||
| actionId: number, | ||
| beforeActionId: number, | ||
| ): ReorderAction { | ||
| return { type: ActionTypes.REORDER_ACTION, actionId, beforeActionId }; | ||
| }, | ||
| jumpToState(index: number): JumpToStateAction { | ||
| jumpToState(this: void, index: number): JumpToStateAction { | ||
| return { type: ActionTypes.JUMP_TO_STATE, index }; | ||
| }, | ||
| jumpToAction(actionId: number): JumpToActionAction { | ||
| jumpToAction(this: void, actionId: number): JumpToActionAction { | ||
| return { type: ActionTypes.JUMP_TO_ACTION, actionId }; | ||
@@ -252,2 +255,3 @@ }, | ||
| importState<S, A extends Action<string>, MonitorState = null>( | ||
| this: void, | ||
| nextLiftedState: LiftedState<S, A, MonitorState> | readonly A[], | ||
@@ -259,7 +263,7 @@ noRecompute?: boolean, | ||
| lockChanges(status: boolean): LockChangesAction { | ||
| lockChanges(this: void, status: boolean): LockChangesAction { | ||
| return { type: ActionTypes.LOCK_CHANGES, status }; | ||
| }, | ||
| pauseRecording(status: boolean): PauseRecordingAction { | ||
| pauseRecording(this: void, status: boolean): PauseRecordingAction { | ||
| return { type: ActionTypes.PAUSE_RECORDING, status }; | ||
@@ -274,4 +278,4 @@ }, | ||
| */ | ||
| function computeWithTryCatch<S, A extends Action<string>>( | ||
| reducer: Reducer<S, A>, | ||
| function computeWithTryCatch<S, A extends Action<string>, PreloadedState>( | ||
| reducer: Reducer<S, A, PreloadedState>, | ||
| action: A, | ||
@@ -285,3 +289,2 @@ state: S, | ||
| } catch (err) { | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| nextError = (err as object).toString(); | ||
@@ -307,4 +310,4 @@ if (isChrome) { | ||
| */ | ||
| function computeNextEntry<S, A extends Action<string>>( | ||
| reducer: Reducer<S, A>, | ||
| function computeNextEntry<S, A extends Action<string>, PreloadedState>( | ||
| reducer: Reducer<S, A, PreloadedState>, | ||
| action: A, | ||
@@ -323,6 +326,6 @@ state: S, | ||
| */ | ||
| function recomputeStates<S, A extends Action<string>>( | ||
| function recomputeStates<S, A extends Action<string>, PreloadedState>( | ||
| computedStates: { state: S; error?: string }[], | ||
| minInvalidatedStateIndex: number, | ||
| reducer: Reducer<S, A>, | ||
| reducer: Reducer<S, A, PreloadedState>, | ||
| committedState: S, | ||
@@ -353,3 +356,3 @@ actionsById: { [actionId: number]: PerformAction<A> }, | ||
| const shouldSkip = skippedActionIds.indexOf(actionId) > -1; | ||
| const shouldSkip = skippedActionIds.includes(actionId); | ||
| let entry; | ||
@@ -386,3 +389,3 @@ if (shouldSkip) { | ||
| traceLimit?: number, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type | ||
| toExcludeFromTrace?: Function, | ||
@@ -423,7 +426,8 @@ ) { | ||
| A extends Action<string>, | ||
| PreloadedState, | ||
| MonitorState, | ||
| MonitorAction extends Action<string>, | ||
| >( | ||
| reducer: Reducer<S, A>, | ||
| initialCommittedState: PreloadedState<S> | undefined, | ||
| reducer: Reducer<S, A, PreloadedState>, | ||
| initialCommittedState: S | PreloadedState | undefined, | ||
| monitorReducer: Reducer<MonitorState, MonitorAction>, | ||
@@ -487,3 +491,3 @@ options: Options<S, A, MonitorState, MonitorAction>, | ||
| skippedActionIds = skippedActionIds.filter( | ||
| (id) => idsToDelete.indexOf(id) === -1, | ||
| (id) => !idsToDelete.includes(id), | ||
| ); | ||
@@ -587,3 +591,3 @@ stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)]; | ||
| // States must be recomputed before committing excess. | ||
| computedStates = recomputeStates<S, A>( | ||
| computedStates = recomputeStates<S, A, PreloadedState>( | ||
| computedStates, | ||
@@ -682,5 +686,14 @@ minInvalidatedStateIndex, | ||
| if (active) { | ||
| skippedActionIds = difference(skippedActionIds, actionIds); | ||
| const actionIdsSet = new Set(actionIds); | ||
| skippedActionIds = skippedActionIds.filter( | ||
| (actionId) => !actionIdsSet.has(actionId), | ||
| ); | ||
| } else { | ||
| skippedActionIds = union(skippedActionIds, actionIds); | ||
| const skippedActionIdsSet = new Set(skippedActionIds); | ||
| skippedActionIds = [ | ||
| ...skippedActionIds, | ||
| ...actionIds.filter( | ||
| (actionId) => !skippedActionIdsSet.has(actionId), | ||
| ), | ||
| ]; | ||
| } | ||
@@ -710,3 +723,6 @@ | ||
| // Forget any actions that are currently being skipped. | ||
| stagedActionIds = difference(stagedActionIds, skippedActionIds); | ||
| const skippedActionIdsSet = new Set(skippedActionIds); | ||
| stagedActionIds = stagedActionIds.filter( | ||
| (actionId) => !skippedActionIdsSet.has(actionId), | ||
| ); | ||
| skippedActionIds = []; | ||
@@ -887,2 +903,3 @@ currentStateIndex = Math.min( | ||
| A extends Action<string>, | ||
| PreloadedState, | ||
| MonitorState, | ||
@@ -898,3 +915,5 @@ MonitorAction extends Action<string>, | ||
| NextExt, | ||
| liftReducer: (r: Reducer<S, A>) => LiftedReducer<S, A, MonitorState>, | ||
| liftReducer: ( | ||
| r: Reducer<S, A, PreloadedState>, | ||
| ) => LiftedReducer<S, A, MonitorState>, | ||
| options: Options<S, A, MonitorState, MonitorAction>, | ||
@@ -941,3 +960,3 @@ ) { | ||
| liftReducer( | ||
| nextReducer as unknown as Reducer<S, A>, | ||
| nextReducer as unknown as Reducer<S, A, PreloadedState>, | ||
| ) as unknown as Reducer< | ||
@@ -1024,10 +1043,13 @@ LiftedState<S, A, MonitorState> & NextStateExt, | ||
| return <NextExt, NextStateExt>( | ||
| return < | ||
| NextExt extends NonNullable<unknown>, | ||
| NextStateExt extends NonNullable<unknown>, | ||
| >( | ||
| createStore: StoreEnhancerStoreCreator<NextExt, NextStateExt>, | ||
| ) => | ||
| <S, A extends Action<string>>( | ||
| reducer: Reducer<S, A>, | ||
| initialState?: PreloadedState<S>, | ||
| <S, A extends Action<string>, PreloadedState>( | ||
| reducer: Reducer<S, A, PreloadedState>, | ||
| initialState?: PreloadedState | undefined, | ||
| ) => { | ||
| function liftReducer(r: Reducer<S, A>) { | ||
| function liftReducer(r: Reducer<S, A, PreloadedState>) { | ||
| if (typeof r !== 'function') { | ||
@@ -1044,3 +1066,9 @@ if (r && typeof (r as { default: unknown }).default === 'function') { | ||
| } | ||
| return liftReducerWith<S, A, MonitorState, MonitorAction>( | ||
| return liftReducerWith< | ||
| S, | ||
| A, | ||
| PreloadedState, | ||
| MonitorState, | ||
| MonitorAction | ||
| >( | ||
| r, | ||
@@ -1077,2 +1105,3 @@ initialState, | ||
| A, | ||
| PreloadedState, | ||
| MonitorState, | ||
@@ -1083,3 +1112,7 @@ MonitorAction, | ||
| >( | ||
| liftedStore, | ||
| liftedStore as Store< | ||
| LiftedState<S, A, MonitorState> & NextStateExt, | ||
| LiftedAction<S, A, MonitorState> | ||
| > & | ||
| NextExt, | ||
| liftReducer, | ||
@@ -1086,0 +1119,0 @@ options as unknown as Options<S, A, MonitorState, MonitorAction>, |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = getSymbolObservable; | ||
| function getSymbolObservable() { | ||
| return typeof Symbol === 'function' && Symbol.observable || '@@observable'; | ||
| } |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.INIT_ACTION = exports.ActionTypes = exports.ActionCreators = void 0; | ||
| exports.instrument = instrument; | ||
| var _difference = _interopRequireDefault(require("lodash/difference")); | ||
| var _union = _interopRequireDefault(require("lodash/union")); | ||
| var _isPlainObject = _interopRequireDefault(require("lodash/isPlainObject")); | ||
| var _getSymbolObservable = _interopRequireDefault(require("./getSymbolObservable")); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| const ActionTypes = exports.ActionTypes = { | ||
| PERFORM_ACTION: 'PERFORM_ACTION', | ||
| RESET: 'RESET', | ||
| ROLLBACK: 'ROLLBACK', | ||
| COMMIT: 'COMMIT', | ||
| SWEEP: 'SWEEP', | ||
| TOGGLE_ACTION: 'TOGGLE_ACTION', | ||
| SET_ACTIONS_ACTIVE: 'SET_ACTIONS_ACTIVE', | ||
| JUMP_TO_STATE: 'JUMP_TO_STATE', | ||
| JUMP_TO_ACTION: 'JUMP_TO_ACTION', | ||
| REORDER_ACTION: 'REORDER_ACTION', | ||
| IMPORT_STATE: 'IMPORT_STATE', | ||
| LOCK_CHANGES: 'LOCK_CHANGES', | ||
| PAUSE_RECORDING: 'PAUSE_RECORDING' | ||
| }; | ||
| const isChrome = typeof window === 'object' && (typeof window.chrome !== 'undefined' || typeof window.process !== 'undefined' && window.process.type === 'renderer'); | ||
| const isChromeOrNode = isChrome || typeof process !== 'undefined' && process.release && process.release.name === 'node'; | ||
| /** | ||
| * Action creators to change the History state. | ||
| */ | ||
| const ActionCreators = exports.ActionCreators = { | ||
| performAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| toExcludeFromTrace) { | ||
| if (!(0, _isPlainObject.default)(action)) { | ||
| throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); | ||
| } | ||
| if (typeof action.type === 'undefined') { | ||
| throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); | ||
| } | ||
| let stack; | ||
| if (trace) { | ||
| let extraFrames = 0; | ||
| if (typeof trace === 'function') { | ||
| stack = trace(action); | ||
| } else { | ||
| const error = Error(); | ||
| let prevStackTraceLimit; | ||
| if (Error.captureStackTrace && isChromeOrNode) { | ||
| // avoid error-polyfill | ||
| if (traceLimit && Error.stackTraceLimit < traceLimit) { | ||
| prevStackTraceLimit = Error.stackTraceLimit; | ||
| Error.stackTraceLimit = traceLimit; | ||
| } | ||
| Error.captureStackTrace(error, toExcludeFromTrace); | ||
| } else { | ||
| extraFrames = 3; | ||
| } | ||
| stack = error.stack; | ||
| if (prevStackTraceLimit) Error.stackTraceLimit = prevStackTraceLimit; | ||
| if (extraFrames || typeof Error.stackTraceLimit !== 'number' || traceLimit && Error.stackTraceLimit > traceLimit) { | ||
| if (stack != null) { | ||
| const frames = stack.split('\n'); | ||
| if (traceLimit && frames.length > traceLimit) { | ||
| stack = frames.slice(0, traceLimit + extraFrames + (frames[0].startsWith('Error') ? 1 : 0)).join('\n'); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| type: ActionTypes.PERFORM_ACTION, | ||
| action, | ||
| timestamp: Date.now(), | ||
| stack | ||
| }; | ||
| }, | ||
| reset() { | ||
| return { | ||
| type: ActionTypes.RESET, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| rollback() { | ||
| return { | ||
| type: ActionTypes.ROLLBACK, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| commit() { | ||
| return { | ||
| type: ActionTypes.COMMIT, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| sweep() { | ||
| return { | ||
| type: ActionTypes.SWEEP | ||
| }; | ||
| }, | ||
| toggleAction(id) { | ||
| return { | ||
| type: ActionTypes.TOGGLE_ACTION, | ||
| id | ||
| }; | ||
| }, | ||
| setActionsActive(start, end) { | ||
| let active = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; | ||
| return { | ||
| type: ActionTypes.SET_ACTIONS_ACTIVE, | ||
| start, | ||
| end, | ||
| active | ||
| }; | ||
| }, | ||
| reorderAction(actionId, beforeActionId) { | ||
| return { | ||
| type: ActionTypes.REORDER_ACTION, | ||
| actionId, | ||
| beforeActionId | ||
| }; | ||
| }, | ||
| jumpToState(index) { | ||
| return { | ||
| type: ActionTypes.JUMP_TO_STATE, | ||
| index | ||
| }; | ||
| }, | ||
| jumpToAction(actionId) { | ||
| return { | ||
| type: ActionTypes.JUMP_TO_ACTION, | ||
| actionId | ||
| }; | ||
| }, | ||
| importState(nextLiftedState, noRecompute) { | ||
| return { | ||
| type: ActionTypes.IMPORT_STATE, | ||
| nextLiftedState, | ||
| noRecompute | ||
| }; | ||
| }, | ||
| lockChanges(status) { | ||
| return { | ||
| type: ActionTypes.LOCK_CHANGES, | ||
| status | ||
| }; | ||
| }, | ||
| pauseRecording(status) { | ||
| return { | ||
| type: ActionTypes.PAUSE_RECORDING, | ||
| status | ||
| }; | ||
| } | ||
| }; | ||
| const INIT_ACTION = exports.INIT_ACTION = { | ||
| type: '@@INIT' | ||
| }; | ||
| /** | ||
| * Computes the next entry with exceptions catching. | ||
| */ | ||
| function computeWithTryCatch(reducer, action, state) { | ||
| let nextState = state; | ||
| let nextError; | ||
| try { | ||
| nextState = reducer(state, action); | ||
| } catch (err) { | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| nextError = err.toString(); | ||
| if (isChrome) { | ||
| // In Chrome, rethrowing provides better source map support | ||
| setTimeout(() => { | ||
| throw err; | ||
| }); | ||
| } else { | ||
| console.error(err); // eslint-disable-line no-console | ||
| } | ||
| } | ||
| return { | ||
| state: nextState, | ||
| error: nextError | ||
| }; | ||
| } | ||
| /** | ||
| * Computes the next entry in the log by applying an action. | ||
| */ | ||
| function computeNextEntry(reducer, action, state, shouldCatchErrors) { | ||
| if (!shouldCatchErrors) { | ||
| return { | ||
| state: reducer(state, action) | ||
| }; | ||
| } | ||
| return computeWithTryCatch(reducer, action, state); | ||
| } | ||
| /** | ||
| * Runs the reducer on invalidated actions to get a fresh computation log. | ||
| */ | ||
| function recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, shouldCatchErrors) { | ||
| // Optimization: exit early and return the same reference | ||
| // if we know nothing could have changed. | ||
| if (!computedStates || minInvalidatedStateIndex === -1 || minInvalidatedStateIndex >= computedStates.length && computedStates.length === stagedActionIds.length) { | ||
| return computedStates; | ||
| } | ||
| const nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex); | ||
| for (let i = minInvalidatedStateIndex; i < stagedActionIds.length; i++) { | ||
| const actionId = stagedActionIds[i]; | ||
| const action = actionsById[actionId].action; | ||
| const previousEntry = nextComputedStates[i - 1]; | ||
| const previousState = previousEntry ? previousEntry.state : committedState; | ||
| const shouldSkip = skippedActionIds.indexOf(actionId) > -1; | ||
| let entry; | ||
| if (shouldSkip) { | ||
| entry = previousEntry; | ||
| } else { | ||
| if (shouldCatchErrors && previousEntry && previousEntry.error) { | ||
| entry = { | ||
| state: previousState, | ||
| error: 'Interrupted by an error up the chain' | ||
| }; | ||
| } else { | ||
| entry = computeNextEntry(reducer, action, previousState, shouldCatchErrors); | ||
| } | ||
| } | ||
| nextComputedStates.push(entry); | ||
| } | ||
| return nextComputedStates; | ||
| } | ||
| /** | ||
| * Lifts an app's action into an action on the lifted store. | ||
| */ | ||
| function liftAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| toExcludeFromTrace) { | ||
| return ActionCreators.performAction(action, trace, traceLimit, toExcludeFromTrace); | ||
| } | ||
| function isArray(nextLiftedState) { | ||
| return Array.isArray(nextLiftedState); | ||
| } | ||
| /** | ||
| * Creates a history state reducer from an app's reducer. | ||
| */ | ||
| function liftReducerWith(reducer, initialCommittedState, monitorReducer, options) { | ||
| const initialLiftedState = { | ||
| monitorState: monitorReducer(undefined, {}), | ||
| nextActionId: 1, | ||
| actionsById: { | ||
| 0: liftAction(INIT_ACTION) | ||
| }, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: initialCommittedState, | ||
| currentStateIndex: 0, | ||
| computedStates: [], | ||
| isLocked: options.shouldStartLocked === true, | ||
| isPaused: options.shouldRecordChanges === false | ||
| }; | ||
| /** | ||
| * Manages how the history actions modify the history state. | ||
| */ | ||
| return (liftedState, liftedAction) => { | ||
| let { | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| isLocked, | ||
| isPaused | ||
| } = liftedState || initialLiftedState; | ||
| if (!liftedState) { | ||
| // Prevent mutating initialLiftedState | ||
| actionsById = { | ||
| ...actionsById | ||
| }; | ||
| } | ||
| function commitExcessActions(n) { | ||
| // Auto-commits n-number of excess actions. | ||
| let excess = n; | ||
| let idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| for (let i = 0; i < idsToDelete.length; i++) { | ||
| if (computedStates[i + 1].error) { | ||
| // Stop if error is found. Commit actions up to error. | ||
| excess = i; | ||
| idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| break; | ||
| } else { | ||
| delete actionsById[idsToDelete[i]]; | ||
| } | ||
| } | ||
| skippedActionIds = skippedActionIds.filter(id => idsToDelete.indexOf(id) === -1); | ||
| stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)]; | ||
| committedState = computedStates[excess].state; | ||
| computedStates = computedStates.slice(excess); | ||
| currentStateIndex = currentStateIndex > excess ? currentStateIndex - excess : 0; | ||
| } | ||
| function computePausedAction(shouldInit) { | ||
| let computedState; | ||
| if (shouldInit) { | ||
| computedState = computedStates[currentStateIndex]; | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| } else { | ||
| computedState = computeNextEntry(reducer, liftedAction.action, computedStates[currentStateIndex].state, false); | ||
| } | ||
| if (!options.pauseActionType || nextActionId === 1) { | ||
| return { | ||
| monitorState, | ||
| actionsById: { | ||
| 0: liftAction(INIT_ACTION) | ||
| }, | ||
| nextActionId: 1, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: computedState.state, | ||
| currentStateIndex: 0, | ||
| computedStates: [computedState], | ||
| isLocked, | ||
| isPaused: true | ||
| }; | ||
| } | ||
| if (shouldInit) { | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| stagedActionIds = [...stagedActionIds, nextActionId]; | ||
| nextActionId++; | ||
| } | ||
| return { | ||
| monitorState, | ||
| actionsById: { | ||
| ...actionsById, | ||
| [nextActionId - 1]: liftAction({ | ||
| type: options.pauseActionType | ||
| }) | ||
| }, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates: [...computedStates.slice(0, stagedActionIds.length - 1), computedState], | ||
| isLocked, | ||
| isPaused: true | ||
| }; | ||
| } | ||
| // By default, aggressively recompute every state whatever happens. | ||
| // This has O(n) performance, so we'll override this to a sensible | ||
| // value whenever we feel like we don't have to recompute the states. | ||
| let minInvalidatedStateIndex = 0; | ||
| // maxAge number can be changed dynamically | ||
| let maxAge = options.maxAge; | ||
| if (typeof maxAge === 'function') maxAge = maxAge(liftedAction, liftedState); | ||
| if (/^@@redux\/(INIT|REPLACE)/.test(liftedAction.type)) { | ||
| if (options.shouldHotReload === false) { | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates.length === 0 ? initialCommittedState : computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| } | ||
| // Recompute states on hot reload and init. | ||
| minInvalidatedStateIndex = 0; | ||
| if (maxAge && stagedActionIds.length > maxAge) { | ||
| // States must be recomputed before committing excess. | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| commitExcessActions(stagedActionIds.length - maxAge); | ||
| // Avoid double computation. | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } else { | ||
| switch (liftedAction.type) { | ||
| case ActionTypes.PERFORM_ACTION: | ||
| { | ||
| if (isLocked) return liftedState || initialLiftedState; | ||
| if (isPaused) return computePausedAction(); | ||
| // Auto-commit as new actions come in. | ||
| if (maxAge && stagedActionIds.length >= maxAge) { | ||
| commitExcessActions(stagedActionIds.length - maxAge + 1); | ||
| } | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| const actionId = nextActionId++; | ||
| // Mutation! This is the hottest path, and we optimize on purpose. | ||
| // It is safe because we set a new key in a cache dictionary. | ||
| actionsById[actionId] = liftedAction; | ||
| stagedActionIds = [...stagedActionIds, actionId]; | ||
| // Optimization: we know that only the new action needs computing. | ||
| minInvalidatedStateIndex = stagedActionIds.length - 1; | ||
| break; | ||
| } | ||
| case ActionTypes.RESET: | ||
| { | ||
| // Get back to the state the store was created with. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = initialCommittedState; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.COMMIT: | ||
| { | ||
| // Consider the last committed state the new starting point. | ||
| // Squash any staged actions into a single committed state. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.ROLLBACK: | ||
| { | ||
| // Forget about any staged actions. | ||
| // Start again from the last committed state. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.TOGGLE_ACTION: | ||
| { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { | ||
| id: actionId | ||
| } = liftedAction; | ||
| const index = skippedActionIds.indexOf(actionId); | ||
| if (index === -1) { | ||
| skippedActionIds = [actionId, ...skippedActionIds]; | ||
| } else { | ||
| skippedActionIds = skippedActionIds.filter(id => id !== actionId); | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(actionId); | ||
| break; | ||
| } | ||
| case ActionTypes.SET_ACTIONS_ACTIVE: | ||
| { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { | ||
| start, | ||
| end, | ||
| active | ||
| } = liftedAction; | ||
| const actionIds = []; | ||
| for (let i = start; i < end; i++) actionIds.push(i); | ||
| if (active) { | ||
| skippedActionIds = (0, _difference.default)(skippedActionIds, actionIds); | ||
| } else { | ||
| skippedActionIds = (0, _union.default)(skippedActionIds, actionIds); | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(start); | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_STATE: | ||
| { | ||
| // Without recomputing anything, move the pointer that tell us | ||
| // which state is considered the current one. Useful for sliders. | ||
| currentStateIndex = liftedAction.index; | ||
| // Optimization: we know the history has not changed. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_ACTION: | ||
| { | ||
| // Jumps to a corresponding state to a specific action. | ||
| // Useful when filtering actions. | ||
| const index = stagedActionIds.indexOf(liftedAction.actionId); | ||
| if (index !== -1) currentStateIndex = index; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.SWEEP: | ||
| { | ||
| // Forget any actions that are currently being skipped. | ||
| stagedActionIds = (0, _difference.default)(stagedActionIds, skippedActionIds); | ||
| skippedActionIds = []; | ||
| currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1); | ||
| break; | ||
| } | ||
| case ActionTypes.REORDER_ACTION: | ||
| { | ||
| // Recompute actions in a new order. | ||
| const actionId = liftedAction.actionId; | ||
| const idx = stagedActionIds.indexOf(actionId); | ||
| // do nothing in case the action is already removed or trying to move the first action | ||
| if (idx < 1) break; | ||
| const beforeActionId = liftedAction.beforeActionId; | ||
| let newIdx = stagedActionIds.indexOf(beforeActionId); | ||
| if (newIdx < 1) { | ||
| // move to the beginning or to the end | ||
| const count = stagedActionIds.length; | ||
| newIdx = beforeActionId > stagedActionIds[count - 1] ? count : 1; | ||
| } | ||
| const diff = idx - newIdx; | ||
| if (diff > 0) { | ||
| // move left | ||
| stagedActionIds = [...stagedActionIds.slice(0, newIdx), actionId, ...stagedActionIds.slice(newIdx, idx), ...stagedActionIds.slice(idx + 1)]; | ||
| minInvalidatedStateIndex = newIdx; | ||
| } else if (diff < 0) { | ||
| // move right | ||
| stagedActionIds = [...stagedActionIds.slice(0, idx), ...stagedActionIds.slice(idx + 1, newIdx), actionId, ...stagedActionIds.slice(newIdx)]; | ||
| minInvalidatedStateIndex = idx; | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.IMPORT_STATE: | ||
| { | ||
| if (isArray(liftedAction.nextLiftedState)) { | ||
| // recompute array of actions | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = liftedAction.nextLiftedState.length; | ||
| computedStates = []; | ||
| committedState = liftedAction.preloadedState; | ||
| minInvalidatedStateIndex = 0; | ||
| // iterate through actions | ||
| liftedAction.nextLiftedState.forEach(action => { | ||
| actionsById[nextActionId] = liftAction(action, options.trace || options.shouldIncludeCallstack); | ||
| stagedActionIds.push(nextActionId); | ||
| nextActionId++; | ||
| }); | ||
| } else { | ||
| // Completely replace everything. | ||
| ({ | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates | ||
| } = liftedAction.nextLiftedState); | ||
| if (liftedAction.noRecompute) { | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.LOCK_CHANGES: | ||
| { | ||
| isLocked = liftedAction.status; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.PAUSE_RECORDING: | ||
| { | ||
| isPaused = liftedAction.status; | ||
| if (isPaused) { | ||
| return computePausedAction(true); | ||
| } | ||
| // Commit when unpausing | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| default: | ||
| { | ||
| // If the action is not recognized, it's a monitor action. | ||
| // Optimization: a monitor action can't change history. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| return { | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| isLocked, | ||
| isPaused | ||
| }; | ||
| }; | ||
| } | ||
| /** | ||
| * Provides an app's view into the state of the lifted store. | ||
| */ | ||
| function unliftState(liftedState) { | ||
| const { | ||
| computedStates, | ||
| currentStateIndex | ||
| } = liftedState; | ||
| const { | ||
| state | ||
| } = computedStates[currentStateIndex]; | ||
| return state; | ||
| } | ||
| /** | ||
| * Provides an app's view into the lifted store. | ||
| */ | ||
| function unliftStore(liftedStore, liftReducer, options) { | ||
| let lastDefinedState; | ||
| const trace = options.trace || options.shouldIncludeCallstack; | ||
| const traceLimit = options.traceLimit || 10; | ||
| function getState() { | ||
| const state = unliftState(liftedStore.getState()); | ||
| if (state !== undefined) { | ||
| lastDefinedState = state; | ||
| } | ||
| return lastDefinedState; | ||
| } | ||
| function dispatch(action) { | ||
| liftedStore.dispatch(liftAction(action, trace, traceLimit, dispatch)); | ||
| return action; | ||
| } | ||
| const $$observable = (0, _getSymbolObservable.default)(); | ||
| if (!($$observable in liftedStore)) { | ||
| console.warn('Symbol.observable as defined by Redux and Redux DevTools do not match. This could cause your app to behave differently if the DevTools are not loaded. Consider polyfilling Symbol.observable before Redux is imported or avoid polyfilling Symbol.observable altogether.'); | ||
| } | ||
| return { | ||
| liftedStore, | ||
| dispatch, | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| subscribe: liftedStore.subscribe, | ||
| getState, | ||
| replaceReducer(nextReducer) { | ||
| liftedStore.replaceReducer(liftReducer(nextReducer)); | ||
| }, | ||
| [$$observable]() { | ||
| return { | ||
| subscribe(observer) { | ||
| if (typeof observer !== 'object') { | ||
| throw new TypeError('Expected the observer to be an object.'); | ||
| } | ||
| function observeState() { | ||
| if (observer.next) { | ||
| observer.next(getState()); | ||
| } | ||
| } | ||
| observeState(); | ||
| const unsubscribe = liftedStore.subscribe(observeState); | ||
| return { | ||
| unsubscribe | ||
| }; | ||
| }, | ||
| [$$observable]() { | ||
| return this; | ||
| } | ||
| }; | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Redux instrumentation store enhancer. | ||
| */ | ||
| function instrument() { | ||
| let monitorReducer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : () => null; | ||
| let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
| if (typeof options.maxAge === 'number' && options.maxAge < 2) { | ||
| throw new Error('DevTools.instrument({ maxAge }) option, if specified, ' + 'may not be less than 2.'); | ||
| } | ||
| return createStore => (reducer, initialState) => { | ||
| function liftReducer(r) { | ||
| if (typeof r !== 'function') { | ||
| if (r && typeof r.default === 'function') { | ||
| throw new Error('Expected the reducer to be a function. ' + 'Instead got an object with a "default" field. ' + 'Did you pass a module instead of the default export? ' + 'Try passing require(...).default instead.'); | ||
| } | ||
| throw new Error('Expected the reducer to be a function.'); | ||
| } | ||
| return liftReducerWith(r, initialState, monitorReducer, options); | ||
| } | ||
| const liftedStore = createStore(liftReducer(reducer)); | ||
| if (liftedStore.liftedStore) { | ||
| throw new Error('DevTools instrumentation should not be applied more than once. ' + 'Check your store configuration.'); | ||
| } | ||
| return unliftStore(liftedStore, liftReducer, options); | ||
| }; | ||
| } |
| export default function getSymbolObservable() { | ||
| return typeof Symbol === 'function' && Symbol.observable || '@@observable'; | ||
| } |
| import difference from 'lodash/difference'; | ||
| import union from 'lodash/union'; | ||
| import isPlainObject from 'lodash/isPlainObject'; | ||
| import getSymbolObservable from './getSymbolObservable'; | ||
| export const ActionTypes = { | ||
| PERFORM_ACTION: 'PERFORM_ACTION', | ||
| RESET: 'RESET', | ||
| ROLLBACK: 'ROLLBACK', | ||
| COMMIT: 'COMMIT', | ||
| SWEEP: 'SWEEP', | ||
| TOGGLE_ACTION: 'TOGGLE_ACTION', | ||
| SET_ACTIONS_ACTIVE: 'SET_ACTIONS_ACTIVE', | ||
| JUMP_TO_STATE: 'JUMP_TO_STATE', | ||
| JUMP_TO_ACTION: 'JUMP_TO_ACTION', | ||
| REORDER_ACTION: 'REORDER_ACTION', | ||
| IMPORT_STATE: 'IMPORT_STATE', | ||
| LOCK_CHANGES: 'LOCK_CHANGES', | ||
| PAUSE_RECORDING: 'PAUSE_RECORDING' | ||
| }; | ||
| const isChrome = typeof window === 'object' && (typeof window.chrome !== 'undefined' || typeof window.process !== 'undefined' && window.process.type === 'renderer'); | ||
| const isChromeOrNode = isChrome || typeof process !== 'undefined' && process.release && process.release.name === 'node'; | ||
| /** | ||
| * Action creators to change the History state. | ||
| */ | ||
| export const ActionCreators = { | ||
| performAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| toExcludeFromTrace) { | ||
| if (!isPlainObject(action)) { | ||
| throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.'); | ||
| } | ||
| if (typeof action.type === 'undefined') { | ||
| throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?'); | ||
| } | ||
| let stack; | ||
| if (trace) { | ||
| let extraFrames = 0; | ||
| if (typeof trace === 'function') { | ||
| stack = trace(action); | ||
| } else { | ||
| const error = Error(); | ||
| let prevStackTraceLimit; | ||
| if (Error.captureStackTrace && isChromeOrNode) { | ||
| // avoid error-polyfill | ||
| if (traceLimit && Error.stackTraceLimit < traceLimit) { | ||
| prevStackTraceLimit = Error.stackTraceLimit; | ||
| Error.stackTraceLimit = traceLimit; | ||
| } | ||
| Error.captureStackTrace(error, toExcludeFromTrace); | ||
| } else { | ||
| extraFrames = 3; | ||
| } | ||
| stack = error.stack; | ||
| if (prevStackTraceLimit) Error.stackTraceLimit = prevStackTraceLimit; | ||
| if (extraFrames || typeof Error.stackTraceLimit !== 'number' || traceLimit && Error.stackTraceLimit > traceLimit) { | ||
| if (stack != null) { | ||
| const frames = stack.split('\n'); | ||
| if (traceLimit && frames.length > traceLimit) { | ||
| stack = frames.slice(0, traceLimit + extraFrames + (frames[0].startsWith('Error') ? 1 : 0)).join('\n'); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| type: ActionTypes.PERFORM_ACTION, | ||
| action, | ||
| timestamp: Date.now(), | ||
| stack | ||
| }; | ||
| }, | ||
| reset() { | ||
| return { | ||
| type: ActionTypes.RESET, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| rollback() { | ||
| return { | ||
| type: ActionTypes.ROLLBACK, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| commit() { | ||
| return { | ||
| type: ActionTypes.COMMIT, | ||
| timestamp: Date.now() | ||
| }; | ||
| }, | ||
| sweep() { | ||
| return { | ||
| type: ActionTypes.SWEEP | ||
| }; | ||
| }, | ||
| toggleAction(id) { | ||
| return { | ||
| type: ActionTypes.TOGGLE_ACTION, | ||
| id | ||
| }; | ||
| }, | ||
| setActionsActive(start, end) { | ||
| let active = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; | ||
| return { | ||
| type: ActionTypes.SET_ACTIONS_ACTIVE, | ||
| start, | ||
| end, | ||
| active | ||
| }; | ||
| }, | ||
| reorderAction(actionId, beforeActionId) { | ||
| return { | ||
| type: ActionTypes.REORDER_ACTION, | ||
| actionId, | ||
| beforeActionId | ||
| }; | ||
| }, | ||
| jumpToState(index) { | ||
| return { | ||
| type: ActionTypes.JUMP_TO_STATE, | ||
| index | ||
| }; | ||
| }, | ||
| jumpToAction(actionId) { | ||
| return { | ||
| type: ActionTypes.JUMP_TO_ACTION, | ||
| actionId | ||
| }; | ||
| }, | ||
| importState(nextLiftedState, noRecompute) { | ||
| return { | ||
| type: ActionTypes.IMPORT_STATE, | ||
| nextLiftedState, | ||
| noRecompute | ||
| }; | ||
| }, | ||
| lockChanges(status) { | ||
| return { | ||
| type: ActionTypes.LOCK_CHANGES, | ||
| status | ||
| }; | ||
| }, | ||
| pauseRecording(status) { | ||
| return { | ||
| type: ActionTypes.PAUSE_RECORDING, | ||
| status | ||
| }; | ||
| } | ||
| }; | ||
| export const INIT_ACTION = { | ||
| type: '@@INIT' | ||
| }; | ||
| /** | ||
| * Computes the next entry with exceptions catching. | ||
| */ | ||
| function computeWithTryCatch(reducer, action, state) { | ||
| let nextState = state; | ||
| let nextError; | ||
| try { | ||
| nextState = reducer(state, action); | ||
| } catch (err) { | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| nextError = err.toString(); | ||
| if (isChrome) { | ||
| // In Chrome, rethrowing provides better source map support | ||
| setTimeout(() => { | ||
| throw err; | ||
| }); | ||
| } else { | ||
| console.error(err); // eslint-disable-line no-console | ||
| } | ||
| } | ||
| return { | ||
| state: nextState, | ||
| error: nextError | ||
| }; | ||
| } | ||
| /** | ||
| * Computes the next entry in the log by applying an action. | ||
| */ | ||
| function computeNextEntry(reducer, action, state, shouldCatchErrors) { | ||
| if (!shouldCatchErrors) { | ||
| return { | ||
| state: reducer(state, action) | ||
| }; | ||
| } | ||
| return computeWithTryCatch(reducer, action, state); | ||
| } | ||
| /** | ||
| * Runs the reducer on invalidated actions to get a fresh computation log. | ||
| */ | ||
| function recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, shouldCatchErrors) { | ||
| // Optimization: exit early and return the same reference | ||
| // if we know nothing could have changed. | ||
| if (!computedStates || minInvalidatedStateIndex === -1 || minInvalidatedStateIndex >= computedStates.length && computedStates.length === stagedActionIds.length) { | ||
| return computedStates; | ||
| } | ||
| const nextComputedStates = computedStates.slice(0, minInvalidatedStateIndex); | ||
| for (let i = minInvalidatedStateIndex; i < stagedActionIds.length; i++) { | ||
| const actionId = stagedActionIds[i]; | ||
| const action = actionsById[actionId].action; | ||
| const previousEntry = nextComputedStates[i - 1]; | ||
| const previousState = previousEntry ? previousEntry.state : committedState; | ||
| const shouldSkip = skippedActionIds.indexOf(actionId) > -1; | ||
| let entry; | ||
| if (shouldSkip) { | ||
| entry = previousEntry; | ||
| } else { | ||
| if (shouldCatchErrors && previousEntry && previousEntry.error) { | ||
| entry = { | ||
| state: previousState, | ||
| error: 'Interrupted by an error up the chain' | ||
| }; | ||
| } else { | ||
| entry = computeNextEntry(reducer, action, previousState, shouldCatchErrors); | ||
| } | ||
| } | ||
| nextComputedStates.push(entry); | ||
| } | ||
| return nextComputedStates; | ||
| } | ||
| /** | ||
| * Lifts an app's action into an action on the lifted store. | ||
| */ | ||
| function liftAction(action, trace, traceLimit, | ||
| // eslint-disable-next-line @typescript-eslint/ban-types | ||
| toExcludeFromTrace) { | ||
| return ActionCreators.performAction(action, trace, traceLimit, toExcludeFromTrace); | ||
| } | ||
| function isArray(nextLiftedState) { | ||
| return Array.isArray(nextLiftedState); | ||
| } | ||
| /** | ||
| * Creates a history state reducer from an app's reducer. | ||
| */ | ||
| function liftReducerWith(reducer, initialCommittedState, monitorReducer, options) { | ||
| const initialLiftedState = { | ||
| monitorState: monitorReducer(undefined, {}), | ||
| nextActionId: 1, | ||
| actionsById: { | ||
| 0: liftAction(INIT_ACTION) | ||
| }, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: initialCommittedState, | ||
| currentStateIndex: 0, | ||
| computedStates: [], | ||
| isLocked: options.shouldStartLocked === true, | ||
| isPaused: options.shouldRecordChanges === false | ||
| }; | ||
| /** | ||
| * Manages how the history actions modify the history state. | ||
| */ | ||
| return (liftedState, liftedAction) => { | ||
| let { | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| isLocked, | ||
| isPaused | ||
| } = liftedState || initialLiftedState; | ||
| if (!liftedState) { | ||
| // Prevent mutating initialLiftedState | ||
| actionsById = { | ||
| ...actionsById | ||
| }; | ||
| } | ||
| function commitExcessActions(n) { | ||
| // Auto-commits n-number of excess actions. | ||
| let excess = n; | ||
| let idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| for (let i = 0; i < idsToDelete.length; i++) { | ||
| if (computedStates[i + 1].error) { | ||
| // Stop if error is found. Commit actions up to error. | ||
| excess = i; | ||
| idsToDelete = stagedActionIds.slice(1, excess + 1); | ||
| break; | ||
| } else { | ||
| delete actionsById[idsToDelete[i]]; | ||
| } | ||
| } | ||
| skippedActionIds = skippedActionIds.filter(id => idsToDelete.indexOf(id) === -1); | ||
| stagedActionIds = [0, ...stagedActionIds.slice(excess + 1)]; | ||
| committedState = computedStates[excess].state; | ||
| computedStates = computedStates.slice(excess); | ||
| currentStateIndex = currentStateIndex > excess ? currentStateIndex - excess : 0; | ||
| } | ||
| function computePausedAction(shouldInit) { | ||
| let computedState; | ||
| if (shouldInit) { | ||
| computedState = computedStates[currentStateIndex]; | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| } else { | ||
| computedState = computeNextEntry(reducer, liftedAction.action, computedStates[currentStateIndex].state, false); | ||
| } | ||
| if (!options.pauseActionType || nextActionId === 1) { | ||
| return { | ||
| monitorState, | ||
| actionsById: { | ||
| 0: liftAction(INIT_ACTION) | ||
| }, | ||
| nextActionId: 1, | ||
| stagedActionIds: [0], | ||
| skippedActionIds: [], | ||
| committedState: computedState.state, | ||
| currentStateIndex: 0, | ||
| computedStates: [computedState], | ||
| isLocked, | ||
| isPaused: true | ||
| }; | ||
| } | ||
| if (shouldInit) { | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| stagedActionIds = [...stagedActionIds, nextActionId]; | ||
| nextActionId++; | ||
| } | ||
| return { | ||
| monitorState, | ||
| actionsById: { | ||
| ...actionsById, | ||
| [nextActionId - 1]: liftAction({ | ||
| type: options.pauseActionType | ||
| }) | ||
| }, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates: [...computedStates.slice(0, stagedActionIds.length - 1), computedState], | ||
| isLocked, | ||
| isPaused: true | ||
| }; | ||
| } | ||
| // By default, aggressively recompute every state whatever happens. | ||
| // This has O(n) performance, so we'll override this to a sensible | ||
| // value whenever we feel like we don't have to recompute the states. | ||
| let minInvalidatedStateIndex = 0; | ||
| // maxAge number can be changed dynamically | ||
| let maxAge = options.maxAge; | ||
| if (typeof maxAge === 'function') maxAge = maxAge(liftedAction, liftedState); | ||
| if (/^@@redux\/(INIT|REPLACE)/.test(liftedAction.type)) { | ||
| if (options.shouldHotReload === false) { | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates.length === 0 ? initialCommittedState : computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| } | ||
| // Recompute states on hot reload and init. | ||
| minInvalidatedStateIndex = 0; | ||
| if (maxAge && stagedActionIds.length > maxAge) { | ||
| // States must be recomputed before committing excess. | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| commitExcessActions(stagedActionIds.length - maxAge); | ||
| // Avoid double computation. | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } else { | ||
| switch (liftedAction.type) { | ||
| case ActionTypes.PERFORM_ACTION: | ||
| { | ||
| if (isLocked) return liftedState || initialLiftedState; | ||
| if (isPaused) return computePausedAction(); | ||
| // Auto-commit as new actions come in. | ||
| if (maxAge && stagedActionIds.length >= maxAge) { | ||
| commitExcessActions(stagedActionIds.length - maxAge + 1); | ||
| } | ||
| if (currentStateIndex === stagedActionIds.length - 1) { | ||
| currentStateIndex++; | ||
| } | ||
| const actionId = nextActionId++; | ||
| // Mutation! This is the hottest path, and we optimize on purpose. | ||
| // It is safe because we set a new key in a cache dictionary. | ||
| actionsById[actionId] = liftedAction; | ||
| stagedActionIds = [...stagedActionIds, actionId]; | ||
| // Optimization: we know that only the new action needs computing. | ||
| minInvalidatedStateIndex = stagedActionIds.length - 1; | ||
| break; | ||
| } | ||
| case ActionTypes.RESET: | ||
| { | ||
| // Get back to the state the store was created with. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = initialCommittedState; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.COMMIT: | ||
| { | ||
| // Consider the last committed state the new starting point. | ||
| // Squash any staged actions into a single committed state. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.ROLLBACK: | ||
| { | ||
| // Forget about any staged actions. | ||
| // Start again from the last committed state. | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| case ActionTypes.TOGGLE_ACTION: | ||
| { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { | ||
| id: actionId | ||
| } = liftedAction; | ||
| const index = skippedActionIds.indexOf(actionId); | ||
| if (index === -1) { | ||
| skippedActionIds = [actionId, ...skippedActionIds]; | ||
| } else { | ||
| skippedActionIds = skippedActionIds.filter(id => id !== actionId); | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(actionId); | ||
| break; | ||
| } | ||
| case ActionTypes.SET_ACTIONS_ACTIVE: | ||
| { | ||
| // Toggle whether an action with given ID is skipped. | ||
| // Being skipped means it is a no-op during the computation. | ||
| const { | ||
| start, | ||
| end, | ||
| active | ||
| } = liftedAction; | ||
| const actionIds = []; | ||
| for (let i = start; i < end; i++) actionIds.push(i); | ||
| if (active) { | ||
| skippedActionIds = difference(skippedActionIds, actionIds); | ||
| } else { | ||
| skippedActionIds = union(skippedActionIds, actionIds); | ||
| } | ||
| // Optimization: we know history before this action hasn't changed | ||
| minInvalidatedStateIndex = stagedActionIds.indexOf(start); | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_STATE: | ||
| { | ||
| // Without recomputing anything, move the pointer that tell us | ||
| // which state is considered the current one. Useful for sliders. | ||
| currentStateIndex = liftedAction.index; | ||
| // Optimization: we know the history has not changed. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.JUMP_TO_ACTION: | ||
| { | ||
| // Jumps to a corresponding state to a specific action. | ||
| // Useful when filtering actions. | ||
| const index = stagedActionIds.indexOf(liftedAction.actionId); | ||
| if (index !== -1) currentStateIndex = index; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.SWEEP: | ||
| { | ||
| // Forget any actions that are currently being skipped. | ||
| stagedActionIds = difference(stagedActionIds, skippedActionIds); | ||
| skippedActionIds = []; | ||
| currentStateIndex = Math.min(currentStateIndex, stagedActionIds.length - 1); | ||
| break; | ||
| } | ||
| case ActionTypes.REORDER_ACTION: | ||
| { | ||
| // Recompute actions in a new order. | ||
| const actionId = liftedAction.actionId; | ||
| const idx = stagedActionIds.indexOf(actionId); | ||
| // do nothing in case the action is already removed or trying to move the first action | ||
| if (idx < 1) break; | ||
| const beforeActionId = liftedAction.beforeActionId; | ||
| let newIdx = stagedActionIds.indexOf(beforeActionId); | ||
| if (newIdx < 1) { | ||
| // move to the beginning or to the end | ||
| const count = stagedActionIds.length; | ||
| newIdx = beforeActionId > stagedActionIds[count - 1] ? count : 1; | ||
| } | ||
| const diff = idx - newIdx; | ||
| if (diff > 0) { | ||
| // move left | ||
| stagedActionIds = [...stagedActionIds.slice(0, newIdx), actionId, ...stagedActionIds.slice(newIdx, idx), ...stagedActionIds.slice(idx + 1)]; | ||
| minInvalidatedStateIndex = newIdx; | ||
| } else if (diff < 0) { | ||
| // move right | ||
| stagedActionIds = [...stagedActionIds.slice(0, idx), ...stagedActionIds.slice(idx + 1, newIdx), actionId, ...stagedActionIds.slice(newIdx)]; | ||
| minInvalidatedStateIndex = idx; | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.IMPORT_STATE: | ||
| { | ||
| if (isArray(liftedAction.nextLiftedState)) { | ||
| // recompute array of actions | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| currentStateIndex = liftedAction.nextLiftedState.length; | ||
| computedStates = []; | ||
| committedState = liftedAction.preloadedState; | ||
| minInvalidatedStateIndex = 0; | ||
| // iterate through actions | ||
| liftedAction.nextLiftedState.forEach(action => { | ||
| actionsById[nextActionId] = liftAction(action, options.trace || options.shouldIncludeCallstack); | ||
| stagedActionIds.push(nextActionId); | ||
| nextActionId++; | ||
| }); | ||
| } else { | ||
| // Completely replace everything. | ||
| ({ | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates | ||
| } = liftedAction.nextLiftedState); | ||
| if (liftedAction.noRecompute) { | ||
| minInvalidatedStateIndex = Infinity; | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| case ActionTypes.LOCK_CHANGES: | ||
| { | ||
| isLocked = liftedAction.status; | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| case ActionTypes.PAUSE_RECORDING: | ||
| { | ||
| isPaused = liftedAction.status; | ||
| if (isPaused) { | ||
| return computePausedAction(true); | ||
| } | ||
| // Commit when unpausing | ||
| actionsById = { | ||
| 0: liftAction(INIT_ACTION) | ||
| }; | ||
| nextActionId = 1; | ||
| stagedActionIds = [0]; | ||
| skippedActionIds = []; | ||
| committedState = computedStates[currentStateIndex].state; | ||
| currentStateIndex = 0; | ||
| computedStates = []; | ||
| break; | ||
| } | ||
| default: | ||
| { | ||
| // If the action is not recognized, it's a monitor action. | ||
| // Optimization: a monitor action can't change history. | ||
| minInvalidatedStateIndex = Infinity; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| computedStates = recomputeStates(computedStates, minInvalidatedStateIndex, reducer, committedState, actionsById, stagedActionIds, skippedActionIds, options.shouldCatchErrors); | ||
| monitorState = monitorReducer(monitorState, liftedAction); | ||
| return { | ||
| monitorState, | ||
| actionsById, | ||
| nextActionId, | ||
| stagedActionIds, | ||
| skippedActionIds, | ||
| committedState, | ||
| currentStateIndex, | ||
| computedStates, | ||
| isLocked, | ||
| isPaused | ||
| }; | ||
| }; | ||
| } | ||
| /** | ||
| * Provides an app's view into the state of the lifted store. | ||
| */ | ||
| function unliftState(liftedState) { | ||
| const { | ||
| computedStates, | ||
| currentStateIndex | ||
| } = liftedState; | ||
| const { | ||
| state | ||
| } = computedStates[currentStateIndex]; | ||
| return state; | ||
| } | ||
| /** | ||
| * Provides an app's view into the lifted store. | ||
| */ | ||
| function unliftStore(liftedStore, liftReducer, options) { | ||
| let lastDefinedState; | ||
| const trace = options.trace || options.shouldIncludeCallstack; | ||
| const traceLimit = options.traceLimit || 10; | ||
| function getState() { | ||
| const state = unliftState(liftedStore.getState()); | ||
| if (state !== undefined) { | ||
| lastDefinedState = state; | ||
| } | ||
| return lastDefinedState; | ||
| } | ||
| function dispatch(action) { | ||
| liftedStore.dispatch(liftAction(action, trace, traceLimit, dispatch)); | ||
| return action; | ||
| } | ||
| const $$observable = getSymbolObservable(); | ||
| if (!($$observable in liftedStore)) { | ||
| console.warn('Symbol.observable as defined by Redux and Redux DevTools do not match. This could cause your app to behave differently if the DevTools are not loaded. Consider polyfilling Symbol.observable before Redux is imported or avoid polyfilling Symbol.observable altogether.'); | ||
| } | ||
| return { | ||
| liftedStore, | ||
| dispatch, | ||
| // eslint-disable-next-line @typescript-eslint/unbound-method | ||
| subscribe: liftedStore.subscribe, | ||
| getState, | ||
| replaceReducer(nextReducer) { | ||
| liftedStore.replaceReducer(liftReducer(nextReducer)); | ||
| }, | ||
| [$$observable]() { | ||
| return { | ||
| subscribe(observer) { | ||
| if (typeof observer !== 'object') { | ||
| throw new TypeError('Expected the observer to be an object.'); | ||
| } | ||
| function observeState() { | ||
| if (observer.next) { | ||
| observer.next(getState()); | ||
| } | ||
| } | ||
| observeState(); | ||
| const unsubscribe = liftedStore.subscribe(observeState); | ||
| return { | ||
| unsubscribe | ||
| }; | ||
| }, | ||
| [$$observable]() { | ||
| return this; | ||
| } | ||
| }; | ||
| } | ||
| }; | ||
| } | ||
| /** | ||
| * Redux instrumentation store enhancer. | ||
| */ | ||
| export function instrument() { | ||
| let monitorReducer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : () => null; | ||
| let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
| if (typeof options.maxAge === 'number' && options.maxAge < 2) { | ||
| throw new Error('DevTools.instrument({ maxAge }) option, if specified, ' + 'may not be less than 2.'); | ||
| } | ||
| return createStore => (reducer, initialState) => { | ||
| function liftReducer(r) { | ||
| if (typeof r !== 'function') { | ||
| if (r && typeof r.default === 'function') { | ||
| throw new Error('Expected the reducer to be a function. ' + 'Instead got an object with a "default" field. ' + 'Did you pass a module instead of the default export? ' + 'Try passing require(...).default instead.'); | ||
| } | ||
| throw new Error('Expected the reducer to be a function.'); | ||
| } | ||
| return liftReducerWith(r, initialState, monitorReducer, options); | ||
| } | ||
| const liftedStore = createStore(liftReducer(reducer)); | ||
| if (liftedStore.liftedStore) { | ||
| throw new Error('DevTools instrumentation should not be applied more than once. ' + 'Check your store configuration.'); | ||
| } | ||
| return unliftStore(liftedStore, liftReducer, options); | ||
| }; | ||
| } |
| export default function getSymbolObservable(): typeof Symbol.observable | "@@observable"; |
| import { Action, Reducer, Store, StoreEnhancer } from 'redux'; | ||
| export declare const ActionTypes: { | ||
| readonly PERFORM_ACTION: "PERFORM_ACTION"; | ||
| readonly RESET: "RESET"; | ||
| readonly ROLLBACK: "ROLLBACK"; | ||
| readonly COMMIT: "COMMIT"; | ||
| readonly SWEEP: "SWEEP"; | ||
| readonly TOGGLE_ACTION: "TOGGLE_ACTION"; | ||
| readonly SET_ACTIONS_ACTIVE: "SET_ACTIONS_ACTIVE"; | ||
| readonly JUMP_TO_STATE: "JUMP_TO_STATE"; | ||
| readonly JUMP_TO_ACTION: "JUMP_TO_ACTION"; | ||
| readonly REORDER_ACTION: "REORDER_ACTION"; | ||
| readonly IMPORT_STATE: "IMPORT_STATE"; | ||
| readonly LOCK_CHANGES: "LOCK_CHANGES"; | ||
| readonly PAUSE_RECORDING: "PAUSE_RECORDING"; | ||
| }; | ||
| export interface PerformAction<A extends Action<string>> { | ||
| type: typeof ActionTypes.PERFORM_ACTION; | ||
| action: A; | ||
| timestamp: number; | ||
| stack: string | undefined; | ||
| } | ||
| interface ResetAction { | ||
| type: typeof ActionTypes.RESET; | ||
| timestamp: number; | ||
| } | ||
| interface RollbackAction { | ||
| type: typeof ActionTypes.ROLLBACK; | ||
| timestamp: number; | ||
| } | ||
| interface CommitAction { | ||
| type: typeof ActionTypes.COMMIT; | ||
| timestamp: number; | ||
| } | ||
| interface SweepAction { | ||
| type: typeof ActionTypes.SWEEP; | ||
| } | ||
| interface ToggleAction { | ||
| type: typeof ActionTypes.TOGGLE_ACTION; | ||
| id: number; | ||
| } | ||
| interface SetActionsActiveAction { | ||
| type: typeof ActionTypes.SET_ACTIONS_ACTIVE; | ||
| start: number; | ||
| end: number; | ||
| active: boolean; | ||
| } | ||
| interface ReorderAction { | ||
| type: typeof ActionTypes.REORDER_ACTION; | ||
| actionId: number; | ||
| beforeActionId: number; | ||
| } | ||
| interface JumpToStateAction { | ||
| type: typeof ActionTypes.JUMP_TO_STATE; | ||
| index: number; | ||
| } | ||
| interface JumpToActionAction { | ||
| type: typeof ActionTypes.JUMP_TO_ACTION; | ||
| actionId: number; | ||
| } | ||
| interface ImportStateAction<S, A extends Action<string>, MonitorState> { | ||
| type: typeof ActionTypes.IMPORT_STATE; | ||
| nextLiftedState: LiftedState<S, A, MonitorState> | readonly A[]; | ||
| preloadedState?: S; | ||
| noRecompute?: boolean | undefined; | ||
| } | ||
| interface LockChangesAction { | ||
| type: typeof ActionTypes.LOCK_CHANGES; | ||
| status: boolean; | ||
| } | ||
| interface PauseRecordingAction { | ||
| type: typeof ActionTypes.PAUSE_RECORDING; | ||
| status: boolean; | ||
| } | ||
| export type LiftedAction<S, A extends Action<string>, MonitorState> = PerformAction<A> | ResetAction | RollbackAction | CommitAction | SweepAction | ToggleAction | SetActionsActiveAction | ReorderAction | JumpToStateAction | JumpToActionAction | ImportStateAction<S, A, MonitorState> | LockChangesAction | PauseRecordingAction; | ||
| /** | ||
| * Action creators to change the History state. | ||
| */ | ||
| export declare const ActionCreators: { | ||
| performAction<A extends Action<string>>(action: A, trace?: boolean | ((action: A) => string | undefined) | undefined, traceLimit?: number, toExcludeFromTrace?: Function): { | ||
| type: "PERFORM_ACTION"; | ||
| action: A; | ||
| timestamp: number; | ||
| stack: string | undefined; | ||
| }; | ||
| reset(): ResetAction; | ||
| rollback(): RollbackAction; | ||
| commit(): CommitAction; | ||
| sweep(): SweepAction; | ||
| toggleAction(id: number): ToggleAction; | ||
| setActionsActive(start: number, end: number, active?: boolean): SetActionsActiveAction; | ||
| reorderAction(actionId: number, beforeActionId: number): ReorderAction; | ||
| jumpToState(index: number): JumpToStateAction; | ||
| jumpToAction(actionId: number): JumpToActionAction; | ||
| importState<S, A_1 extends Action<string>, MonitorState = null>(nextLiftedState: LiftedState<S, A_1, MonitorState> | readonly A_1[], noRecompute?: boolean): ImportStateAction<S, A_1, MonitorState>; | ||
| lockChanges(status: boolean): LockChangesAction; | ||
| pauseRecording(status: boolean): PauseRecordingAction; | ||
| }; | ||
| export declare const INIT_ACTION: { | ||
| type: string; | ||
| }; | ||
| export interface LiftedState<S, A extends Action<string>, MonitorState> { | ||
| monitorState: MonitorState; | ||
| nextActionId: number; | ||
| actionsById: { | ||
| [actionId: number]: PerformAction<A>; | ||
| }; | ||
| stagedActionIds: number[]; | ||
| skippedActionIds: number[]; | ||
| committedState: S; | ||
| currentStateIndex: number; | ||
| computedStates: { | ||
| state: S; | ||
| error?: string; | ||
| }[]; | ||
| isLocked: boolean; | ||
| isPaused: boolean; | ||
| } | ||
| export type LiftedReducer<S, A extends Action<string>, MonitorState> = Reducer<LiftedState<S, A, MonitorState>, LiftedAction<S, A, MonitorState>>; | ||
| export type LiftedStore<S, A extends Action<string>, MonitorState> = Store<LiftedState<S, A, MonitorState>, LiftedAction<S, A, MonitorState>>; | ||
| export type InstrumentExt<S, A extends Action<string>, MonitorState> = { | ||
| liftedStore: LiftedStore<S, A, MonitorState>; | ||
| }; | ||
| export type EnhancedStore<S, A extends Action<string>, MonitorState> = Store<S, A> & InstrumentExt<S, A, MonitorState>; | ||
| export interface Options<S, A extends Action<string>, MonitorState, MonitorAction extends Action<string>> { | ||
| maxAge?: number | ((currentLiftedAction: LiftedAction<S, A, MonitorState>, previousLiftedState: LiftedState<S, A, MonitorState> | undefined) => number); | ||
| shouldCatchErrors?: boolean; | ||
| shouldRecordChanges?: boolean; | ||
| pauseActionType?: unknown; | ||
| shouldStartLocked?: boolean; | ||
| shouldHotReload?: boolean; | ||
| trace?: boolean | ((action: A) => string | undefined); | ||
| traceLimit?: number; | ||
| shouldIncludeCallstack?: boolean; | ||
| } | ||
| /** | ||
| * Redux instrumentation store enhancer. | ||
| */ | ||
| export declare function instrument<OptionsS, OptionsA extends Action<string>, MonitorState = null, MonitorAction extends Action<string> = never>(monitorReducer?: Reducer<MonitorState, MonitorAction>, options?: Options<OptionsS, OptionsA, MonitorState, MonitorAction>): StoreEnhancer<InstrumentExt<any, any, MonitorState>>; | ||
| export {}; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
1
-66.67%9
-55%Yes
NaN73050
-22.11%9
-18.18%1822
-28.3%1
Infinity%- Removed
- Removed
- Removed
- Removed