More improvements to types

This commit is contained in:
Nathan Bierema 2021-07-18 17:44:48 -04:00
parent 0915714127
commit 9af6e54649
4 changed files with 111 additions and 40 deletions

View File

@ -44,31 +44,41 @@ export const noFiltersApplied = (localFilter: LocalFilter | undefined) =>
!window.devToolsOptions.filter || !window.devToolsOptions.filter ||
window.devToolsOptions.filter === FilterState.DO_NOT_FILTER); window.devToolsOptions.filter === FilterState.DO_NOT_FILTER);
export function isFiltered(action, localFilter: LocalFilter | undefined) { export function isFiltered<A extends Action<unknown>>(
action: A | string,
localFilter: LocalFilter | undefined
) {
if ( if (
noFiltersApplied(localFilter) || noFiltersApplied(localFilter) ||
(typeof action !== 'string' && typeof action.type.match !== 'function') (typeof action !== 'string' &&
typeof (action.type as string).match !== 'function')
) { ) {
return false; return false;
} }
const { whitelist, blacklist } = localFilter || window.devToolsOptions || {}; const { whitelist, blacklist } = localFilter || window.devToolsOptions || {};
const actionType = action.type || action; const actionType = ((action as A).type || action) as string;
return ( return (
(whitelist && !actionType.match(whitelist)) || (whitelist && !actionType.match(whitelist)) ||
(blacklist && actionType.match(blacklist)) (blacklist && actionType.match(blacklist))
); );
} }
function filterActions(actionsById, actionSanitizer) { function filterActions<A extends Action<unknown>>(
actionsById: { [p: number]: PerformAction<A> },
actionSanitizer: ((action: A, id: number) => A) | undefined
) {
if (!actionSanitizer) return actionsById; if (!actionSanitizer) return actionsById;
return mapValues(actionsById, (action, id) => ({ return mapValues(actionsById, (action, id: number) => ({
...action, ...action,
action: actionSanitizer(action.action, id), action: actionSanitizer(action.action, id),
})); }));
} }
function filterStates(computedStates, stateSanitizer) { function filterStates<S>(
computedStates: { state: S; error?: string | undefined }[],
stateSanitizer: ((state: S, index: number) => S) | undefined
) {
if (!stateSanitizer) return computedStates; if (!stateSanitizer) return computedStates;
return computedStates.map((state, idx) => ({ return computedStates.map((state, idx) => ({
...state, ...state,

View File

@ -7,9 +7,16 @@ import importState from './importState';
import generateId from './generateInstanceId'; import generateId from './generateInstanceId';
import { Config } from '../../browser/extension/inject/pageScript'; import { Config } from '../../browser/extension/inject/pageScript';
import { Action } from 'redux'; import { Action } from 'redux';
import { LiftedState, PerformAction } from '@redux-devtools/instrument'; import {
EnhancedStore,
LiftedState,
PerformAction,
} from '@redux-devtools/instrument';
import { LibConfig } from '@redux-devtools/app/lib/actions'; import { LibConfig } from '@redux-devtools/app/lib/actions';
import { ContentScriptToPageScriptMessage } from '../../browser/extension/inject/contentScript'; import {
ContentScriptToPageScriptMessage,
ListenerMessage,
} from '../../browser/extension/inject/contentScript';
import { Position } from './openWindow'; import { Position } from './openWindow';
const listeners: { const listeners: {
@ -267,19 +274,23 @@ function getStackTrace(
return stack; return stack;
} }
function amendActionType( function amendActionType<A extends Action<unknown>>(
action, action: A | StructuralPerformAction<A> | string,
config, config: Config,
toExcludeFromTrace: Function | undefined toExcludeFromTrace: Function | undefined
) { ): StructuralPerformAction<A> {
let timestamp = Date.now(); let timestamp = Date.now();
let stack = getStackTrace(config, toExcludeFromTrace); let stack = getStackTrace(config, toExcludeFromTrace);
if (typeof action === 'string') { if (typeof action === 'string') {
return { action: { type: action }, timestamp, stack }; return { action: { type: action } as A, timestamp, stack };
} }
if (!action.type) return { action: { type: 'update' }, timestamp, stack }; if (!(action as A).type)
if (action.action) return stack ? { stack, ...action } : action; return { action: { type: 'update' } as A, timestamp, stack };
return { action, timestamp, stack }; if ((action as StructuralPerformAction<A>).action)
return (
stack ? { stack, ...action } : action
) as StructuralPerformAction<A>;
return { action, timestamp, stack } as StructuralPerformAction<A>;
} }
interface LiftedMessage { interface LiftedMessage {
@ -305,12 +316,26 @@ interface ExportMessage<S, A extends Action<unknown>> {
readonly instanceId: number; readonly instanceId: number;
} }
interface StructuralPerformAction<A extends Action<unknown>> {
readonly action: A;
readonly timestamp?: number;
readonly stack?: string;
}
type SingleUserAction<A extends Action<unknown>> =
| PerformAction<A>
| StructuralPerformAction<A>
| A;
type UserAction<A extends Action<unknown>> =
| SingleUserAction<A>
| readonly SingleUserAction<A>[];
interface ActionMessage<S, A extends Action<unknown>> { interface ActionMessage<S, A extends Action<unknown>> {
readonly type: 'ACTION'; readonly type: 'ACTION';
readonly payload: S; readonly payload: S;
readonly source: typeof source; readonly source: typeof source;
readonly instanceId: number; readonly instanceId: number;
readonly action: PerformAction<A> | A; readonly action: UserAction<A>;
readonly maxAge: number; readonly maxAge: number;
readonly nextActionId: number; readonly nextActionId: number;
} }
@ -407,9 +432,9 @@ export function toContentScript<S, A extends Action<unknown>>(
} }
} }
export function sendMessage( export function sendMessage<S, A extends Action<unknown>>(
action, action: StructuralPerformAction<A> | StructuralPerformAction<A>[],
state, state: S,
config: Config, config: Config,
instanceId?: number, instanceId?: number,
name?: string name?: string
@ -459,18 +484,22 @@ export function setListener(
} }
const liftListener = const liftListener =
(listener, config: Config) => (message: ContentScriptToPageScriptMessage) => { <S, A extends Action<unknown>>(
let data = {}; listener: (message: ListenerMessage<S, A>) => void,
config: Config
) =>
(message: ContentScriptToPageScriptMessage) => {
if (message.type === 'IMPORT') { if (message.type === 'IMPORT') {
data.type = 'DISPATCH'; listener({
data.payload = { type: 'DISPATCH',
payload: {
type: 'IMPORT_STATE', type: 'IMPORT_STATE',
...importState(message.state, config), ...importState(message.state, config),
}; },
});
} else { } else {
data = message; listener(message);
} }
listener(data);
}; };
export function disconnect() { export function disconnect() {
@ -493,8 +522,8 @@ export function connect(preConfig: Config) {
const localFilter = getLocalFilter(config); const localFilter = getLocalFilter(config);
const autoPause = config.autoPause; const autoPause = config.autoPause;
let isPaused = autoPause; let isPaused = autoPause;
let delayedActions = []; let delayedActions: StructuralPerformAction<Action<unknown>>[] = [];
let delayedStates = []; let delayedStates: unknown[] = [];
const rootListener = (action: ContentScriptToPageScriptMessage) => { const rootListener = (action: ContentScriptToPageScriptMessage) => {
if (autoPause) { if (autoPause) {
@ -517,7 +546,9 @@ export function connect(preConfig: Config) {
listeners[id] = [rootListener]; listeners[id] = [rootListener];
const subscribe = (listener) => { const subscribe = <S, A extends Action<unknown>>(
listener: (message: ListenerMessage<S, A>) => void
) => {
if (!listener) return undefined; if (!listener) return undefined;
const liftedListener = liftListener(listener, config); const liftedListener = liftListener(listener, config);
const listenersForId = listeners[id] as (( const listenersForId = listeners[id] as ((
@ -541,7 +572,7 @@ export function connect(preConfig: Config) {
delayedStates = []; delayedStates = [];
}, latency); }, latency);
const send = (action, state) => { const send = <S, A extends Action<unknown>>(action: A, state: S) => {
if ( if (
isPaused || isPaused ||
isFiltered(action, localFilter) || isFiltered(action, localFilter) ||
@ -550,7 +581,7 @@ export function connect(preConfig: Config) {
return; return;
} }
let amendedAction = action; let amendedAction: A | StructuralPerformAction<A> = action;
const amendedState = config.stateSanitizer const amendedState = config.stateSanitizer
? config.stateSanitizer(state) ? config.stateSanitizer(state)
: state; : state;
@ -561,7 +592,7 @@ export function connect(preConfig: Config) {
amendedAction = { amendedAction = {
action: { type: amendedAction }, action: { type: amendedAction },
timestamp: Date.now(), timestamp: Date.now(),
}; } as unknown as A;
} }
} else if (config.actionSanitizer) { } else if (config.actionSanitizer) {
amendedAction = config.actionSanitizer(action); amendedAction = config.actionSanitizer(action);
@ -624,8 +655,15 @@ export function connect(preConfig: Config) {
}; };
} }
export function updateStore(stores) { export function updateStore<S, A extends Action<unknown>>(
return function (newStore, instanceId) { stores: {
[K in string | number]: EnhancedStore<S, Action<A>, unknown>;
}
) {
return function (
newStore: EnhancedStore<S, Action<A>, unknown>,
instanceId: number
) {
/* eslint-disable no-console */ /* eslint-disable no-console */
console.warn( console.warn(
'`__REDUX_DEVTOOLS_EXTENSION__.updateStore` is deprecated, remove it and just use ' + '`__REDUX_DEVTOOLS_EXTENSION__.updateStore` is deprecated, remove it and just use ' +

View File

@ -14,6 +14,7 @@ import {
CustomAction, CustomAction,
DispatchAction as AppDispatchAction, DispatchAction as AppDispatchAction,
} from '@redux-devtools/app/lib/actions'; } from '@redux-devtools/app/lib/actions';
import { LiftedState } from '@redux-devtools/instrument';
const source = '@devtools-extension'; const source = '@devtools-extension';
const pageSource = '@devtools-page'; const pageSource = '@devtools-page';
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts // Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
@ -90,6 +91,27 @@ export type ContentScriptToPageScriptMessage =
| ExportAction | ExportAction
| UpdateAction; | UpdateAction;
interface ImportStatePayload<S, A extends Action<unknown>> {
readonly type: 'IMPORT_STATE';
readonly nextLiftedState: LiftedState<S, A, unknown> | readonly A[];
readonly preloadedState?: S;
}
interface ImportStateDispatchAction<S, A extends Action<unknown>> {
readonly type: 'DISPATCH';
readonly payload: ImportStatePayload<S, A>;
}
export type ListenerMessage<S, A extends Action<unknown>> =
| StartAction
| StopAction
| DispatchAction
| ImportAction
| ActionAction
| ExportAction
| UpdateAction
| ImportStateDispatchAction<S, A>;
function postToPageScript(message: ContentScriptToPageScriptMessage) { function postToPageScript(message: ContentScriptToPageScriptMessage) {
window.postMessage(message, '*'); window.postMessage(message, '*');
} }

View File

@ -47,7 +47,7 @@ import { Features } from '@redux-devtools/app/lib/reducers/instances';
const source = '@devtools-page'; const source = '@devtools-page';
let stores: { let stores: {
[instanceId: number]: EnhancedStore<unknown, Action<unknown>, unknown>; [K in string | number]: EnhancedStore<unknown, Action<unknown>, unknown>;
} = {}; } = {};
let reportId: string | null | undefined; let reportId: string | null | undefined;
@ -80,12 +80,12 @@ export interface ConfigWithExpandedMaxAge {
readonly statesFilter?: <S>(state: S, index?: number) => S; readonly statesFilter?: <S>(state: S, index?: number) => S;
readonly actionsFilter?: <A extends Action<unknown>>( readonly actionsFilter?: <A extends Action<unknown>>(
action: A, action: A,
id: number id?: number
) => A; ) => A;
readonly stateSanitizer?: <S>(state: S, index?: number) => S; readonly stateSanitizer?: <S>(state: S, index?: number) => S;
readonly actionSanitizer?: <A extends Action<unknown>>( readonly actionSanitizer?: <A extends Action<unknown>>(
action: A, action: A,
id: number id?: number
) => A; ) => A;
readonly predicate?: <S, A extends Action<unknown>>( readonly predicate?: <S, A extends Action<unknown>>(
state: S, state: S,
@ -114,6 +114,7 @@ export interface ConfigWithExpandedMaxAge {
readonly autoPause?: boolean; readonly autoPause?: boolean;
readonly features?: Features; readonly features?: Features;
readonly type?: string; readonly type?: string;
readonly getActionType?: <A extends Action<unknown>>(action: A) => A;
} }
export interface Config extends ConfigWithExpandedMaxAge { export interface Config extends ConfigWithExpandedMaxAge {