From 82c8f142e673f45e72ffd4040bec998a51198ee6 Mon Sep 17 00:00:00 2001 From: Nathan Bierema Date: Fri, 16 Jul 2021 23:57:57 -0400 Subject: [PATCH] stash --- extension/src/app/api/filters.ts | 38 ++++++---- extension/src/app/api/importState.ts | 2 +- extension/src/app/api/index.ts | 54 +++++++++++--- extension/src/app/middlewares/api.ts | 21 +++++- .../src/app/middlewares/instanceSelector.ts | 8 ++- extension/src/app/middlewares/panelSync.ts | 2 +- extension/src/app/middlewares/windowSync.ts | 37 ++++++---- .../app/reducers/background/persistStates.ts | 4 +- extension/src/app/service/Monitor.ts | 3 +- extension/src/app/stores/enhancerStore.ts | 25 ++++--- .../browser/extension/inject/contentScript.ts | 7 +- .../browser/extension/inject/pageScript.ts | 72 +++++++++++-------- .../src/instrument.ts | 2 +- 13 files changed, 182 insertions(+), 93 deletions(-) diff --git a/extension/src/app/api/filters.ts b/extension/src/app/api/filters.ts index 4f13a367..f4f6374d 100644 --- a/extension/src/app/api/filters.ts +++ b/extension/src/app/api/filters.ts @@ -37,14 +37,14 @@ export function getLocalFilter(config: Config): LocalFilter | undefined { return undefined; } -export const noFiltersApplied = (localFilter) => +export const noFiltersApplied = (localFilter: LocalFilter | undefined) => // !predicate && !localFilter && (!window.devToolsOptions || !window.devToolsOptions.filter || window.devToolsOptions.filter === FilterState.DO_NOT_FILTER); -export function isFiltered(action, localFilter) { +export function isFiltered(action, localFilter: LocalFilter | undefined) { if ( noFiltersApplied(localFilter) || (typeof action !== 'string' && typeof action.type.match !== 'function') @@ -76,23 +76,25 @@ function filterStates(computedStates, stateSanitizer) { })); } -export function filterState( - state, +export function filterState>( + state: LiftedState, type, - localFilter, - stateSanitizer, - actionSanitizer, - nextActionId, - predicate + localFilter: LocalFilter | undefined, + stateSanitizer: ((state: S, index: number) => S) | undefined, + actionSanitizer: ((action: A, id: number) => A) | undefined, + nextActionId: number | undefined, + predicate: ((state: S, action: A) => boolean) | undefined ) { if (type === 'ACTION') { return !stateSanitizer ? state : stateSanitizer(state, nextActionId - 1); } else if (type !== 'STATE') return state; if (predicate || !noFiltersApplied(localFilter)) { - const filteredStagedActionIds = []; - const filteredComputedStates = []; - const sanitizedActionsById = actionSanitizer && {}; + const filteredStagedActionIds: number[] = []; + const filteredComputedStates: { state: S; error?: string | undefined }[] = + []; + const sanitizedActionsById: { [p: number]: PerformAction } | undefined = + actionSanitizer && {}; const { actionsById } = state; const { computedStates } = state; @@ -114,7 +116,7 @@ export function filterState( : liftedState ); if (actionSanitizer) { - sanitizedActionsById[id] = { + sanitizedActionsById![id] = { ...liftedAction, action: actionSanitizer(currAction, id), }; @@ -137,6 +139,14 @@ export function filterState( }; } +export interface PartialLiftedState> { + readonly actionsById: { [actionId: number]: PerformAction }; + readonly computedStates: { state: S; error?: string }[]; + readonly stagedActionIds: readonly number[]; + readonly currentStateIndex: number; + readonly nextActionId: number; +} + export function startingFrom>( sendingActionId: number, state: LiftedState, @@ -148,7 +158,7 @@ export function startingFrom>( predicate: | (>(state: S, action: A) => boolean) | undefined -) { +): LiftedState | PartialLiftedState | undefined { const stagedActionIds = state.stagedActionIds; if (sendingActionId <= stagedActionIds[1]) return state; const index = stagedActionIds.indexOf(sendingActionId); diff --git a/extension/src/app/api/importState.ts b/extension/src/app/api/importState.ts index 02ae7447..74f8da51 100644 --- a/extension/src/app/api/importState.ts +++ b/extension/src/app/api/importState.ts @@ -2,7 +2,7 @@ import mapValues from 'lodash/mapValues'; import jsan from 'jsan'; import seralizeImmutable from '@redux-devtools/serialize/lib/immutable/serialize'; -function deprecate(param) { +function deprecate(param: string) { // eslint-disable-next-line no-console console.warn( `\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1` diff --git a/extension/src/app/api/index.ts b/extension/src/app/api/index.ts index fc98e74c..24d24e9d 100644 --- a/extension/src/app/api/index.ts +++ b/extension/src/app/api/index.ts @@ -2,11 +2,12 @@ import jsan, { Options } from 'jsan'; import throttle from 'lodash/throttle'; import serializeImmutable from '@redux-devtools/serialize/lib/immutable/serialize'; import { getActionsArray } from '@redux-devtools/utils'; -import { getLocalFilter, isFiltered } from './filters'; +import { getLocalFilter, isFiltered, PartialLiftedState } from './filters'; import importState from './importState'; import generateId from './generateInstanceId'; import { PageScriptToContentScriptMessage } from '../../browser/extension/inject/contentScript'; import { Config } from '../../browser/extension/inject/pageScript'; +import { Action } from 'redux'; const listeners = {}; export const source = '@devtools-page'; @@ -109,7 +110,10 @@ function post(message: PageScriptToContentScriptMessage) { window.postMessage(message, '*'); } -function getStackTrace(config, toExcludeFromTrace: Function | undefined) { +function getStackTrace( + config: Config, + toExcludeFromTrace: Function | undefined +) { if (!config.trace) return undefined; if (typeof config.trace === 'function') return config.trace(); @@ -119,9 +123,9 @@ function getStackTrace(config, toExcludeFromTrace: Function | undefined) { const traceLimit = config.traceLimit; const error = Error(); if (Error.captureStackTrace) { - if (Error.stackTraceLimit < traceLimit) { + if (Error.stackTraceLimit < traceLimit!) { prevStackTraceLimit = Error.stackTraceLimit; - Error.stackTraceLimit = traceLimit; + Error.stackTraceLimit = traceLimit!; } Error.captureStackTrace(error, toExcludeFromTrace); } else { @@ -132,12 +136,12 @@ function getStackTrace(config, toExcludeFromTrace: Function | undefined) { if ( extraFrames || typeof Error.stackTraceLimit !== 'number' || - Error.stackTraceLimit > traceLimit + Error.stackTraceLimit > traceLimit! ) { const frames = stack!.split('\n'); - if (frames.length > traceLimit) { + if (frames.length > traceLimit!) { stack = frames - .slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0)) + .slice(0, traceLimit! + extraFrames + (frames[0] === 'Error' ? 1 : 0)) .join('\n'); } } @@ -159,10 +163,38 @@ function amendActionType( return { action, timestamp, stack }; } -export function toContentScript( - message, - serializeState: Serialize | undefined, - serializeAction: Serialize | undefined +interface LiftedMessage { + readonly type: 'LIFTED'; + readonly liftedState: { readonly isPaused: boolean }; + readonly instanceId: number; + readonly source: typeof source; +} + +interface PartialStateMessage> { + readonly type: 'PARTIAL_STATE'; + readonly payload: PartialLiftedState; + readonly source: typeof source; + readonly instanceId: number; + readonly maxAge: number; +} + +interface ExportMessage> { + readonly type: 'EXPORT'; + readonly payload: readonly A[]; + readonly committedState: S; + readonly source: typeof source; + readonly instanceId: number; +} + +type ToContentScriptMessage> = + | LiftedMessage + | PartialStateMessage + | ExportMessage; + +export function toContentScript>( + message: ToContentScriptMessage, + serializeState?: Serialize | undefined, + serializeAction?: Serialize | undefined ) { if (message.type === 'ACTION') { message.action = stringify(message.action, serializeAction); diff --git a/extension/src/app/middlewares/api.ts b/extension/src/app/middlewares/api.ts index f861318a..306d1789 100644 --- a/extension/src/app/middlewares/api.ts +++ b/extension/src/app/middlewares/api.ts @@ -31,7 +31,7 @@ interface UpdateStateAction { readonly type: typeof UPDATE_STATE; } -type TabMessage = StartAction | StopAction | OptionsMessage; +export type TabMessage = StartAction | StopAction | OptionsMessage; type PanelMessage = NAAction; type MonitorMessage = UpdateStateAction; @@ -80,7 +80,22 @@ function toMonitors( }); } -function toContentScript({ message, action, id, instanceId, state }) { +interface ImportMessage { + readonly message: 'IMPORT'; + readonly id: string | number; + readonly instanceId: string; + readonly state: string; +} + +type ToContentScriptMessage = ImportMessage; + +function toContentScript({ + message, + action, + id, + instanceId, + state, +}: ToContentScriptMessage) { connections.tab[id].postMessage({ type: message, action, @@ -251,7 +266,7 @@ function onConnect(port: chrome.runtime.Port) { id = getId(port.sender!); if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`; connections.tab[id] = port; - listener = (msg: TabToBackgroundMessage) => { + listener = (msg) => { if (msg.name === 'INIT_INSTANCE') { if (typeof id === 'number') { chrome.pageAction.show(id); diff --git a/extension/src/app/middlewares/instanceSelector.ts b/extension/src/app/middlewares/instanceSelector.ts index cc89f97c..fd87bd93 100644 --- a/extension/src/app/middlewares/instanceSelector.ts +++ b/extension/src/app/middlewares/instanceSelector.ts @@ -1,4 +1,4 @@ -import { Dispatch, Store } from 'redux'; +import { Dispatch, MiddlewareAPI } from 'redux'; import { SELECT_INSTANCE, UPDATE_STATE, @@ -8,7 +8,7 @@ import { StoreState } from '@redux-devtools/app/lib/reducers'; function selectInstance( tabId: number, - store: Store, + store: MiddlewareAPI, StoreState>, next: Dispatch ) { const instances = store.getState().instances; @@ -33,7 +33,9 @@ function getCurrentTabId(next: (tabId: number) => void) { ); } -export default function popupSelector(store: Store) { +export default function popupSelector( + store: MiddlewareAPI, StoreState> +) { return (next: Dispatch) => (action: StoreAction) => { const result = next(action); if (action.type === UPDATE_STATE) { diff --git a/extension/src/app/middlewares/panelSync.ts b/extension/src/app/middlewares/panelSync.ts index aae303ad..7a3c6187 100644 --- a/extension/src/app/middlewares/panelSync.ts +++ b/extension/src/app/middlewares/panelSync.ts @@ -4,7 +4,7 @@ import { SELECT_INSTANCE, } from '@redux-devtools/app/lib/constants/actionTypes'; import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances'; -import { Dispatch, MiddlewareAPI, Store } from 'redux'; +import { Dispatch, MiddlewareAPI } from 'redux'; import { StoreState } from '@redux-devtools/app/lib/reducers'; import { StoreAction } from '@redux-devtools/app/lib/actions'; diff --git a/extension/src/app/middlewares/windowSync.ts b/extension/src/app/middlewares/windowSync.ts index 442d3162..afc79ffa 100644 --- a/extension/src/app/middlewares/windowSync.ts +++ b/extension/src/app/middlewares/windowSync.ts @@ -3,21 +3,28 @@ import { LIFTED_ACTION, } from '@redux-devtools/app/lib/constants/actionTypes'; import { getActiveInstance } from '@redux-devtools/app/lib/reducers/instances'; +import { Dispatch, MiddlewareAPI, Store } from 'redux'; +import { BackgroundState } from '../reducers/background'; +import { StoreAction } from '@redux-devtools/app/lib/actions'; -const syncStores = (baseStore) => (store) => (next) => (action) => { - if (action.type === UPDATE_STATE) { - return next({ - ...action, - instances: baseStore.getState().instances, - }); - } - if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') { - const instances = store.getState().instances; - const instanceId = getActiveInstance(instances); - const id = instances.options[instanceId].connectionId; - baseStore.dispatch({ ...action, instanceId, id }); - } - return next(action); -}; +const syncStores = + (baseStore: Store) => + (store: MiddlewareAPI>) => + (next: Dispatch) => + (action: StoreAction) => { + if (action.type === UPDATE_STATE) { + return next({ + ...action, + instances: baseStore.getState().instances, + }); + } + if (action.type === LIFTED_ACTION || action.type === 'TOGGLE_PERSIST') { + const instances = store.getState().instances; + const instanceId = getActiveInstance(instances); + const id = instances.options[instanceId].connectionId; + baseStore.dispatch({ ...action, instanceId, id }); + } + return next(action); + }; export default syncStores; diff --git a/extension/src/app/reducers/background/persistStates.ts b/extension/src/app/reducers/background/persistStates.ts index 0505da33..6ef4a36a 100644 --- a/extension/src/app/reducers/background/persistStates.ts +++ b/extension/src/app/reducers/background/persistStates.ts @@ -1,4 +1,6 @@ -export default function persistStates(state = false, action) { +import { StoreAction } from '@redux-devtools/app/lib/actions'; + +export default function persistStates(state = false, action: StoreAction) { if (action.type === 'TOGGLE_PERSIST') return !state; return state; } diff --git a/extension/src/app/service/Monitor.ts b/extension/src/app/service/Monitor.ts index 67bf411f..210e288d 100644 --- a/extension/src/app/service/Monitor.ts +++ b/extension/src/app/service/Monitor.ts @@ -1,5 +1,6 @@ import { Action } from 'redux'; import { LiftedState } from '@redux-devtools/instrument'; +import { StoreAction } from '@redux-devtools/app/lib/actions'; declare global { interface Window { @@ -23,7 +24,7 @@ export default class Monitor> { ) { this.update = update; } - reducer = (state = {}, action) => { + reducer = (state = {}, action: StoreAction) => { if (!this.active) return state; this.lastAction = action.type; if (action.type === 'LOCK_CHANGES') { diff --git a/extension/src/app/stores/enhancerStore.ts b/extension/src/app/stores/enhancerStore.ts index 715c5654..75d83cd6 100644 --- a/extension/src/app/stores/enhancerStore.ts +++ b/extension/src/app/stores/enhancerStore.ts @@ -1,13 +1,7 @@ import { Action, compose, Reducer, StoreEnhancerStoreCreator } from 'redux'; -import instrument, { - LiftedAction, - LiftedState, -} from '@redux-devtools/instrument'; +import instrument from '@redux-devtools/instrument'; import persistState from '@redux-devtools/core/lib/persistState'; -import { - Config, - ConfigWithExpandedMaxAge, -} from '../../browser/extension/inject/pageScript'; +import { ConfigWithExpandedMaxAge } from '../../browser/extension/inject/pageScript'; export function getUrlParam(key: string) { const matches = window.location.href.match( @@ -16,9 +10,20 @@ export function getUrlParam(key: string) { return matches && matches.length > 0 ? matches[1] : null; } -export default function configureStore( +declare global { + interface Window { + shouldCatchErrors?: boolean; + } +} + +export default function configureStore< + S, + A extends Action, + MonitorState, + MonitorAction extends Action +>( next: StoreEnhancerStoreCreator, - monitorReducer: Reducer, + monitorReducer: Reducer, config: ConfigWithExpandedMaxAge ) { return compose( diff --git a/extension/src/browser/extension/inject/contentScript.ts b/extension/src/browser/extension/inject/contentScript.ts index f243b6df..963e7b3c 100644 --- a/extension/src/browser/extension/inject/contentScript.ts +++ b/extension/src/browser/extension/inject/contentScript.ts @@ -3,6 +3,7 @@ import { getOptionsFromBg, isAllowed, } from '../options/syncOptions'; +import { TabMessage } from '../../../app/middlewares/api'; const source = '@devtools-extension'; const pageSource = '@devtools-page'; // Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts @@ -27,8 +28,8 @@ function connect() { } // Relay background script messages to the page script - bg.onMessage.addListener((message) => { - if (message.action) { + bg.onMessage.addListener((message: TabMessage) => { + if ('action' in message) { window.postMessage( { type: message.type, @@ -39,7 +40,7 @@ function connect() { }, '*' ); - } else if (message.options) { + } else if ('options' in message) { injectOptions(message.options); } else { window.postMessage( diff --git a/extension/src/browser/extension/inject/pageScript.ts b/extension/src/browser/extension/inject/pageScript.ts index a3828748..046688a0 100644 --- a/extension/src/browser/extension/inject/pageScript.ts +++ b/extension/src/browser/extension/inject/pageScript.ts @@ -1,7 +1,15 @@ import { getActionsArray, evalAction } from '@redux-devtools/utils'; import throttle from 'lodash/throttle'; -import { Action, PreloadedState, Reducer, Store, StoreEnhancer } from 'redux'; +import { + Action, + PreloadedState, + Reducer, + Store, + StoreEnhancer, + StoreEnhancerStoreCreator, +} from 'redux'; import Immutable from 'immutable'; +import { EnhancedStore } from '@redux-devtools/instrument'; import createStore from '../../../app/stores/createStore'; import configureStore, { getUrlParam } from '../../../app/stores/enhancerStore'; import { isAllowed, Options } from '../options/syncOptions'; @@ -29,14 +37,15 @@ import { Serialize, } from '../../../app/api'; import { - InstrumentExt, LiftedAction, LiftedState, PerformAction, } from '@redux-devtools/instrument'; const source = '@devtools-page'; -let stores: { [instanceId: number]: Store> } = {}; +let stores: { + [instanceId: number]: EnhancedStore, unknown>; +} = {}; let reportId: string | null | undefined; function deprecateParam(oldParam: string, newParam: string) { @@ -89,9 +98,7 @@ export interface ConfigWithExpandedMaxAge { currentLiftedAction: LiftedAction, previousLiftedState: LiftedState | undefined ) => number); - readonly trace?: - | boolean - | (>(action: A) => string | undefined); + readonly trace?: boolean | (() => string | undefined); readonly traceLimit?: number; readonly shouldCatchErrors?: boolean; readonly shouldHotReload?: boolean; @@ -113,7 +120,7 @@ interface ReduxDevtoolsExtension { preloadedState?: PreloadedState, config?: Config ): Store; - (config: Config): StoreEnhancer; + (config?: Config): StoreEnhancer; open: (position?: Position) => void; notifyErrors: (onError: () => boolean) => void; disconnect: () => void; @@ -125,15 +132,13 @@ declare global { } } -const __REDUX_DEVTOOLS_EXTENSION__ = reduxDevtoolsExtension; - -function reduxDevtoolsExtension>( +function __REDUX_DEVTOOLS_EXTENSION__>( reducer?: Reducer, preloadedState?: PreloadedState, config?: Config ): Store; -function reduxDevtoolsExtension(config: Config): StoreEnhancer; -function reduxDevtoolsExtension>( +function __REDUX_DEVTOOLS_EXTENSION__(config: Config): StoreEnhancer; +function __REDUX_DEVTOOLS_EXTENSION__>( reducer?: Reducer | Config | undefined, preloadedState?: PreloadedState, config?: Config @@ -146,7 +151,7 @@ function reduxDevtoolsExtension>( /* eslint-enable no-param-reassign */ if (!window.devToolsOptions) window.devToolsOptions = {}; - let store: Store & InstrumentExt; + let store: EnhancedStore; let errorOccurred = false; let maxAge: number | undefined; let actionCreators; @@ -313,7 +318,7 @@ function reduxDevtoolsExtension>( ); sendingActionId = nextActionId; if (typeof payload === 'undefined') return; - if (typeof payload.skippedActionIds !== 'undefined') { + if ('skippedActionIds' in payload) { relay('STATE', payload); return; } @@ -494,23 +499,30 @@ function reduxDevtoolsExtension>( relayState(liftedState); } - const enhance = (): StoreEnhancer => (next) => { - return (reducer_, initialState_) => { - if (!isAllowed(window.devToolsOptions)) { - return next(reducer_, initialState_); - } + const enhance = + () => + ( + next: StoreEnhancerStoreCreator + ) => { + return ( + reducer_: Reducer, + initialState_?: PreloadedState + ) => { + if (!isAllowed(window.devToolsOptions)) { + return next(reducer_, initialState_); + } - store = stores[instanceId] = configureStore(next, monitor.reducer, { - ...config, - maxAge: getMaxAge, - })(reducer_, initialState_); + return configureStore(next, monitor.reducer, { + ...config, + maxAge: getMaxAge, + })(reducer_, initialState_); - if (isInIframe()) setTimeout(init, 3000); - else init(); + if (isInIframe()) setTimeout(init, 3000); + else init(); - return store; + return store; + }; }; - }; if (!reducer) return enhance(); /* eslint-disable no-console */ @@ -538,8 +550,10 @@ window.__REDUX_DEVTOOLS_EXTENSION__.connect = connect; window.__REDUX_DEVTOOLS_EXTENSION__.disconnect = disconnect; const preEnhancer = - (instanceId) => (next) => (reducer, preloadedState, enhancer) => { - const store = next(reducer, preloadedState, enhancer); + (instanceId: number): StoreEnhancer => + (next) => + (reducer, preloadedState) => { + const store = next(reducer, preloadedState); if (stores[instanceId]) { stores[instanceId].initialDispatch = store.dispatch; diff --git a/packages/redux-devtools-instrument/src/instrument.ts b/packages/redux-devtools-instrument/src/instrument.ts index 4f6d35aa..91a7772f 100644 --- a/packages/redux-devtools-instrument/src/instrument.ts +++ b/packages/redux-devtools-instrument/src/instrument.ts @@ -100,7 +100,7 @@ interface ImportStateAction, MonitorState> { type: typeof ActionTypes.IMPORT_STATE; nextLiftedState: LiftedState | readonly A[]; preloadedState?: S; - noRecompute: boolean | undefined; + noRecompute?: boolean | undefined; } interface LockChangesAction {