mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 08:30:02 +03:00
stash
This commit is contained in:
parent
7b4698225b
commit
a3df1df2eb
|
@ -1,4 +1,7 @@
|
||||||
import mapValues from 'lodash/mapValues';
|
import mapValues from 'lodash/mapValues';
|
||||||
|
import { Config } from '../../browser/extension/inject/pageScript';
|
||||||
|
import { Action } from 'redux';
|
||||||
|
import { LiftedState, PerformAction } from '@redux-devtools/instrument';
|
||||||
|
|
||||||
export type FilterStateValue =
|
export type FilterStateValue =
|
||||||
| 'DO_NOT_FILTER'
|
| 'DO_NOT_FILTER'
|
||||||
|
@ -11,13 +14,22 @@ export const FilterState: { [K in FilterStateValue]: FilterStateValue } = {
|
||||||
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
WHITELIST_SPECIFIC: 'WHITELIST_SPECIFIC',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getLocalFilter(config) {
|
function isArray(arg: unknown): arg is readonly unknown[] {
|
||||||
|
return Array.isArray(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocalFilter {
|
||||||
|
readonly whitelist: string | undefined;
|
||||||
|
readonly blacklist: string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLocalFilter(config: Config): LocalFilter | undefined {
|
||||||
if (config.actionsBlacklist || config.actionsWhitelist) {
|
if (config.actionsBlacklist || config.actionsWhitelist) {
|
||||||
return {
|
return {
|
||||||
whitelist: Array.isArray(config.actionsWhitelist)
|
whitelist: isArray(config.actionsWhitelist)
|
||||||
? config.actionsWhitelist.join('|')
|
? config.actionsWhitelist.join('|')
|
||||||
: config.actionsWhitelist,
|
: config.actionsWhitelist,
|
||||||
blacklist: Array.isArray(config.actionsBlacklist)
|
blacklist: isArray(config.actionsBlacklist)
|
||||||
? config.actionsBlacklist.join('|')
|
? config.actionsBlacklist.join('|')
|
||||||
: config.actionsBlacklist,
|
: config.actionsBlacklist,
|
||||||
};
|
};
|
||||||
|
@ -125,13 +137,17 @@ export function filterState(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startingFrom(
|
export function startingFrom<S, A extends Action<unknown>>(
|
||||||
sendingActionId,
|
sendingActionId: number,
|
||||||
state,
|
state: LiftedState<S, A, unknown>,
|
||||||
localFilter,
|
localFilter: LocalFilter | undefined,
|
||||||
stateSanitizer,
|
stateSanitizer: (<S>(state: S, index: number) => S) | undefined,
|
||||||
actionSanitizer,
|
actionSanitizer:
|
||||||
predicate
|
| (<A extends Action<unknown>>(action: A, id: number) => A)
|
||||||
|
| undefined,
|
||||||
|
predicate:
|
||||||
|
| (<S, A extends Action<unknown>>(state: S, action: A) => boolean)
|
||||||
|
| undefined
|
||||||
) {
|
) {
|
||||||
const stagedActionIds = state.stagedActionIds;
|
const stagedActionIds = state.stagedActionIds;
|
||||||
if (sendingActionId <= stagedActionIds[1]) return state;
|
if (sendingActionId <= stagedActionIds[1]) return state;
|
||||||
|
@ -142,7 +158,7 @@ export function startingFrom(
|
||||||
const filteredStagedActionIds = shouldFilter ? [0] : stagedActionIds;
|
const filteredStagedActionIds = shouldFilter ? [0] : stagedActionIds;
|
||||||
const actionsById = state.actionsById;
|
const actionsById = state.actionsById;
|
||||||
const computedStates = state.computedStates;
|
const computedStates = state.computedStates;
|
||||||
const newActionsById = {};
|
const newActionsById: { [key: number]: PerformAction<A> } = {};
|
||||||
const newComputedStates = [];
|
const newComputedStates = [];
|
||||||
let key;
|
let key;
|
||||||
let currAction;
|
let currAction;
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
let id = 0;
|
|
||||||
|
|
||||||
export default function generateId(instanceId) {
|
|
||||||
return instanceId || ++id;
|
|
||||||
}
|
|
5
extension/src/app/api/generateInstanceId.ts
Normal file
5
extension/src/app/api/generateInstanceId.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
let id = 0;
|
||||||
|
|
||||||
|
export default function generateId(instanceId: number | undefined) {
|
||||||
|
return instanceId || ++id;
|
||||||
|
}
|
|
@ -1,22 +1,24 @@
|
||||||
import jsan from 'jsan';
|
import jsan, { Options } from 'jsan';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
import seralizeImmutable from '@redux-devtools/serialize/lib/immutable/serialize';
|
import serializeImmutable from '@redux-devtools/serialize/lib/immutable/serialize';
|
||||||
import { getActionsArray } from '@redux-devtools/utils';
|
import { getActionsArray } from '@redux-devtools/utils';
|
||||||
import { getLocalFilter, isFiltered } from './filters';
|
import { getLocalFilter, isFiltered } from './filters';
|
||||||
import importState from './importState';
|
import importState from './importState';
|
||||||
import generateId from './generateInstanceId';
|
import generateId from './generateInstanceId';
|
||||||
|
import { PageScriptToContentScriptMessage } from '../../browser/extension/inject/contentScript';
|
||||||
|
import { Config } from '../../browser/extension/inject/pageScript';
|
||||||
|
|
||||||
const listeners = {};
|
const listeners = {};
|
||||||
export const source = '@devtools-page';
|
export const source = '@devtools-page';
|
||||||
|
|
||||||
function windowReplacer(key, value) {
|
function windowReplacer(key: string, value: unknown) {
|
||||||
if (value && value.window === value) {
|
if (value && (value as Window).window === value) {
|
||||||
return '[WINDOW]';
|
return '[WINDOW]';
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryCatchStringify(obj) {
|
function tryCatchStringify(obj: unknown) {
|
||||||
try {
|
try {
|
||||||
return JSON.stringify(obj);
|
return JSON.stringify(obj);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -25,19 +27,19 @@ function tryCatchStringify(obj) {
|
||||||
console.log('Failed to stringify', err);
|
console.log('Failed to stringify', err);
|
||||||
}
|
}
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
return jsan.stringify(obj, windowReplacer, null, {
|
return jsan.stringify(obj, windowReplacer, undefined, {
|
||||||
circular: '[CIRCULAR]',
|
circular: '[CIRCULAR]',
|
||||||
date: true,
|
date: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stringifyWarned;
|
let stringifyWarned: boolean;
|
||||||
function stringify(obj, serialize) {
|
function stringify(obj: unknown, serialize?: Serialize | undefined) {
|
||||||
const str =
|
const str =
|
||||||
typeof serialize === 'undefined'
|
typeof serialize === 'undefined'
|
||||||
? tryCatchStringify(obj)
|
? tryCatchStringify(obj)
|
||||||
: jsan.stringify(obj, serialize.replacer, null, serialize.options);
|
: jsan.stringify(obj, serialize.replacer, undefined, serialize.options);
|
||||||
|
|
||||||
if (!stringifyWarned && str && str.length > 16 * 1024 * 1024) {
|
if (!stringifyWarned && str && str.length > 16 * 1024 * 1024) {
|
||||||
// 16 MB
|
// 16 MB
|
||||||
|
@ -52,12 +54,21 @@ function stringify(obj, serialize) {
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSeralizeParameter(config, param) {
|
export interface Serialize {
|
||||||
|
readonly replacer?: (key: string, value: unknown) => unknown;
|
||||||
|
readonly reviver?: (key: string, value: unknown) => unknown;
|
||||||
|
readonly options?: Options | boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSerializeParameter(
|
||||||
|
config: Config,
|
||||||
|
param?: 'serializeState' | 'serializeAction'
|
||||||
|
) {
|
||||||
const serialize = config.serialize;
|
const serialize = config.serialize;
|
||||||
if (serialize) {
|
if (serialize) {
|
||||||
if (serialize === true) return { options: true };
|
if (serialize === true) return { options: true };
|
||||||
if (serialize.immutable) {
|
if (serialize.immutable) {
|
||||||
const immutableSerializer = seralizeImmutable(
|
const immutableSerializer = serializeImmutable(
|
||||||
serialize.immutable,
|
serialize.immutable,
|
||||||
serialize.refs,
|
serialize.refs,
|
||||||
serialize.replacer,
|
serialize.replacer,
|
||||||
|
@ -82,23 +93,23 @@ export function getSeralizeParameter(config, param) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const value = config[param];
|
const value = config[param!];
|
||||||
if (typeof value === 'undefined') return undefined;
|
if (typeof value === 'undefined') return undefined;
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.warn(
|
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`
|
`\`${param}\` parameter for Redux DevTools Extension is deprecated. Use \`serialize\` parameter instead: https://github.com/zalmoxisus/redux-devtools-extension/releases/tag/v2.12.1`
|
||||||
);
|
);
|
||||||
|
|
||||||
if (typeof serializeState === 'boolean') return { options: value };
|
if (typeof value === 'boolean') return { options: value };
|
||||||
if (typeof serializeState === 'function') return { replacer: value };
|
if (typeof value === 'function') return { replacer: value };
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function post(message) {
|
function post(message: PageScriptToContentScriptMessage) {
|
||||||
window.postMessage(message, '*');
|
window.postMessage(message, '*');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStackTrace(config, toExcludeFromTrace) {
|
function getStackTrace(config, toExcludeFromTrace: Function | undefined) {
|
||||||
if (!config.trace) return undefined;
|
if (!config.trace) return undefined;
|
||||||
if (typeof config.trace === 'function') return config.trace();
|
if (typeof config.trace === 'function') return config.trace();
|
||||||
|
|
||||||
|
@ -123,7 +134,7 @@ function getStackTrace(config, toExcludeFromTrace) {
|
||||||
typeof Error.stackTraceLimit !== 'number' ||
|
typeof Error.stackTraceLimit !== 'number' ||
|
||||||
Error.stackTraceLimit > traceLimit
|
Error.stackTraceLimit > traceLimit
|
||||||
) {
|
) {
|
||||||
const frames = stack.split('\n');
|
const frames = stack!.split('\n');
|
||||||
if (frames.length > traceLimit) {
|
if (frames.length > traceLimit) {
|
||||||
stack = frames
|
stack = frames
|
||||||
.slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0))
|
.slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0))
|
||||||
|
@ -133,7 +144,11 @@ function getStackTrace(config, toExcludeFromTrace) {
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
function amendActionType(action, config, toExcludeFromTrace) {
|
function amendActionType(
|
||||||
|
action,
|
||||||
|
config,
|
||||||
|
toExcludeFromTrace: Function | undefined
|
||||||
|
) {
|
||||||
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') {
|
||||||
|
@ -144,7 +159,11 @@ function amendActionType(action, config, toExcludeFromTrace) {
|
||||||
return { action, timestamp, stack };
|
return { action, timestamp, stack };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function toContentScript(message, serializeState, serializeAction) {
|
export function toContentScript(
|
||||||
|
message,
|
||||||
|
serializeState: Serialize | undefined,
|
||||||
|
serializeAction: Serialize | undefined
|
||||||
|
) {
|
||||||
if (message.type === 'ACTION') {
|
if (message.type === 'ACTION') {
|
||||||
message.action = stringify(message.action, serializeAction);
|
message.action = stringify(message.action, serializeAction);
|
||||||
message.payload = stringify(message.payload, serializeState);
|
message.payload = stringify(message.payload, serializeState);
|
||||||
|
@ -235,7 +254,7 @@ export function connect(preConfig) {
|
||||||
config.name =
|
config.name =
|
||||||
document.title && id === 1 ? document.title : `Instance ${id}`;
|
document.title && id === 1 ? document.title : `Instance ${id}`;
|
||||||
}
|
}
|
||||||
if (config.serialize) config.serialize = getSeralizeParameter(config);
|
if (config.serialize) config.serialize = getSerializeParameter(config);
|
||||||
const actionCreators = config.actionCreators || {};
|
const actionCreators = config.actionCreators || {};
|
||||||
const latency = config.latency;
|
const latency = config.latency;
|
||||||
const predicate = config.predicate;
|
const predicate = config.predicate;
|
||||||
|
@ -245,7 +264,7 @@ export function connect(preConfig) {
|
||||||
let delayedActions = [];
|
let delayedActions = [];
|
||||||
let delayedStates = [];
|
let delayedStates = [];
|
||||||
|
|
||||||
const rootListiner = (action) => {
|
const rootListener = (action) => {
|
||||||
if (autoPause) {
|
if (autoPause) {
|
||||||
if (action.type === 'START') isPaused = false;
|
if (action.type === 'START') isPaused = false;
|
||||||
else if (action.type === 'STOP') isPaused = true;
|
else if (action.type === 'STOP') isPaused = true;
|
||||||
|
@ -264,7 +283,7 @@ export function connect(preConfig) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
listeners[id] = [rootListiner];
|
listeners[id] = [rootListener];
|
||||||
|
|
||||||
const subscribe = (listener) => {
|
const subscribe = (listener) => {
|
||||||
if (!listener) return undefined;
|
if (!listener) return undefined;
|
|
@ -1,9 +1,9 @@
|
||||||
let handleError;
|
let handleError: () => boolean;
|
||||||
let lastTime = 0;
|
let lastTime = 0;
|
||||||
|
|
||||||
function createExpBackoffTimer(step) {
|
function createExpBackoffTimer(step: number) {
|
||||||
let count = 1;
|
let count = 1;
|
||||||
return function (reset) {
|
return function (reset?: boolean) {
|
||||||
// Reset call
|
// Reset call
|
||||||
if (reset) {
|
if (reset) {
|
||||||
count = 1;
|
count = 1;
|
||||||
|
@ -18,7 +18,7 @@ function createExpBackoffTimer(step) {
|
||||||
|
|
||||||
const nextErrorTimeout = createExpBackoffTimer(5000);
|
const nextErrorTimeout = createExpBackoffTimer(5000);
|
||||||
|
|
||||||
function postError(message) {
|
function postError(message: string) {
|
||||||
if (handleError && !handleError()) return;
|
if (handleError && !handleError()) return;
|
||||||
window.postMessage(
|
window.postMessage(
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,7 @@ function postError(message) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function catchErrors(e) {
|
function catchErrors(e: ErrorEvent) {
|
||||||
if (
|
if (
|
||||||
(window.devToolsOptions && !window.devToolsOptions.shouldCatchErrors) ||
|
(window.devToolsOptions && !window.devToolsOptions.shouldCatchErrors) ||
|
||||||
e.timeStamp - lastTime < nextErrorTimeout()
|
e.timeStamp - lastTime < nextErrorTimeout()
|
||||||
|
@ -42,7 +42,7 @@ function catchErrors(e) {
|
||||||
postError(e.message);
|
postError(e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function notifyErrors(onError) {
|
export default function notifyErrors(onError: () => boolean) {
|
||||||
handleError = onError;
|
handleError = onError;
|
||||||
window.addEventListener('error', catchErrors, false);
|
window.addEventListener('error', catchErrors, false);
|
||||||
}
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
export default function openWindow(position) {
|
export type Position = 'left' | 'right' | 'bottom' | 'panel' | 'remote';
|
||||||
|
|
||||||
|
export default function openWindow(position?: Position) {
|
||||||
window.postMessage(
|
window.postMessage(
|
||||||
{
|
{
|
||||||
source: '@devtools-page',
|
source: '@devtools-page',
|
|
@ -5,13 +5,53 @@ import {
|
||||||
LIFTED_ACTION,
|
LIFTED_ACTION,
|
||||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||||
import { nonReduxDispatch } from '@redux-devtools/app/lib/utils/monitorActions';
|
import { nonReduxDispatch } from '@redux-devtools/app/lib/utils/monitorActions';
|
||||||
import syncOptions from '../../browser/extension/options/syncOptions';
|
import syncOptions, {
|
||||||
|
OptionsMessage,
|
||||||
|
SyncOptions,
|
||||||
|
} from '../../browser/extension/options/syncOptions';
|
||||||
import openDevToolsWindow from '../../browser/extension/background/openWindow';
|
import openDevToolsWindow from '../../browser/extension/background/openWindow';
|
||||||
import { getReport } from '../../browser/extension/background/logging';
|
import { getReport } from '../../browser/extension/background/logging';
|
||||||
|
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
|
||||||
|
interface StartAction {
|
||||||
|
readonly type: 'START';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StopAction {
|
||||||
|
readonly type: 'STOP';
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NAAction {
|
||||||
|
readonly type: 'NA';
|
||||||
|
readonly id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateStateAction {
|
||||||
|
readonly type: typeof UPDATE_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
type TabMessage = StartAction | StopAction | OptionsMessage;
|
||||||
|
type PanelMessage = NAAction;
|
||||||
|
type MonitorMessage = UpdateStateAction;
|
||||||
|
|
||||||
|
type TabPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
|
postMessage: (message: TabMessage) => void;
|
||||||
|
};
|
||||||
|
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
|
postMessage: (message: PanelMessage) => void;
|
||||||
|
};
|
||||||
|
type MonitorPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
|
postMessage: (message: MonitorMessage) => void;
|
||||||
|
};
|
||||||
|
|
||||||
const CONNECTED = 'socket/CONNECTED';
|
const CONNECTED = 'socket/CONNECTED';
|
||||||
const DISCONNECTED = 'socket/DISCONNECTED';
|
const DISCONNECTED = 'socket/DISCONNECTED';
|
||||||
const connections = {
|
const connections: {
|
||||||
|
readonly tab: { [K in number | string]: TabPort };
|
||||||
|
readonly panel: { [K in number | string]: PanelPort };
|
||||||
|
readonly monitor: { [K in number | string]: MonitorPort };
|
||||||
|
} = {
|
||||||
tab: {},
|
tab: {},
|
||||||
panel: {},
|
panel: {},
|
||||||
monitor: {},
|
monitor: {},
|
||||||
|
@ -20,10 +60,16 @@ const chunks = {};
|
||||||
let monitors = 0;
|
let monitors = 0;
|
||||||
let isMonitored = false;
|
let isMonitored = false;
|
||||||
|
|
||||||
const getId = (sender, name) =>
|
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
||||||
sender.tab ? sender.tab.id : name || sender.id;
|
sender.tab ? sender.tab.id! : name || sender.id!;
|
||||||
|
|
||||||
function toMonitors(action, tabId, verbose) {
|
type MonitorAction = NAAction;
|
||||||
|
|
||||||
|
function toMonitors(
|
||||||
|
action: MonitorAction,
|
||||||
|
tabId?: string | number,
|
||||||
|
verbose?: boolean
|
||||||
|
) {
|
||||||
Object.keys(connections.monitor).forEach((id) => {
|
Object.keys(connections.monitor).forEach((id) => {
|
||||||
connections.monitor[id].postMessage(
|
connections.monitor[id].postMessage(
|
||||||
verbose || action.type === 'ERROR' ? action : { type: UPDATE_STATE }
|
verbose || action.type === 'ERROR' ? action : { type: UPDATE_STATE }
|
||||||
|
@ -43,16 +89,18 @@ function toContentScript({ message, action, id, instanceId, state }) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function toAllTabs(msg) {
|
function toAllTabs(msg: TabMessage) {
|
||||||
const tabs = connections.tab;
|
const tabs = connections.tab;
|
||||||
Object.keys(tabs).forEach((id) => {
|
Object.keys(tabs).forEach((id) => {
|
||||||
tabs[id].postMessage(msg);
|
tabs[id].postMessage(msg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function monitorInstances(shouldMonitor, id) {
|
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||||
if (!id && isMonitored === shouldMonitor) return;
|
if (!id && isMonitored === shouldMonitor) return;
|
||||||
const action = { type: shouldMonitor ? 'START' : 'STOP' };
|
const action = {
|
||||||
|
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
||||||
|
};
|
||||||
if (id) {
|
if (id) {
|
||||||
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,8 +128,15 @@ function togglePersist() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BackgroundStoreMessage = unknown;
|
||||||
|
type BackgroundStoreResponse = never;
|
||||||
|
|
||||||
// Receive messages from content scripts
|
// Receive messages from content scripts
|
||||||
function messaging(request, sender, sendResponse) {
|
function messaging(
|
||||||
|
request: BackgroundStoreMessage,
|
||||||
|
sender: chrome.runtime.MessageSender,
|
||||||
|
sendResponse: (response?: BackgroundStoreResponse) => void
|
||||||
|
) {
|
||||||
let tabId = getId(sender);
|
let tabId = getId(sender);
|
||||||
if (!tabId) return;
|
if (!tabId) return;
|
||||||
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
if (sender.frameId) tabId = `${tabId}-${sender.frameId}`;
|
||||||
|
@ -164,7 +219,11 @@ function messaging(request, sender, sendResponse) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function disconnect(type, id, listener) {
|
function disconnect(
|
||||||
|
type: 'tab' | 'monitor' | 'panel',
|
||||||
|
id: number | string,
|
||||||
|
listener?: (message: any, port: chrome.runtime.Port) => void
|
||||||
|
) {
|
||||||
return function disconnectListener() {
|
return function disconnectListener() {
|
||||||
const p = connections[type][id];
|
const p = connections[type][id];
|
||||||
if (listener && p) p.onMessage.removeListener(listener);
|
if (listener && p) p.onMessage.removeListener(listener);
|
||||||
|
@ -182,17 +241,17 @@ function disconnect(type, id, listener) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onConnect(port) {
|
function onConnect(port: chrome.runtime.Port) {
|
||||||
let id;
|
let id: number | string;
|
||||||
let listener;
|
let listener;
|
||||||
|
|
||||||
window.store.dispatch({ type: CONNECTED, port });
|
window.store.dispatch({ type: CONNECTED, port });
|
||||||
|
|
||||||
if (port.name === 'tab') {
|
if (port.name === 'tab') {
|
||||||
id = getId(port.sender);
|
id = getId(port.sender!);
|
||||||
if (port.sender.frameId) id = `${id}-${port.sender.frameId}`;
|
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
||||||
connections.tab[id] = port;
|
connections.tab[id] = port;
|
||||||
listener = (msg) => {
|
listener = (msg: TabToBackgroundMessage) => {
|
||||||
if (msg.name === 'INIT_INSTANCE') {
|
if (msg.name === 'INIT_INSTANCE') {
|
||||||
if (typeof id === 'number') {
|
if (typeof id === 'number') {
|
||||||
chrome.pageAction.show(id);
|
chrome.pageAction.show(id);
|
||||||
|
@ -218,24 +277,24 @@ function onConnect(port) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.name === 'RELAY') {
|
if (msg.name === 'RELAY') {
|
||||||
messaging(msg.message, port.sender, id);
|
messaging(msg.message, port.sender!, id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
port.onMessage.addListener(listener);
|
port.onMessage.addListener(listener);
|
||||||
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
||||||
} else if (port.name && port.name.indexOf('monitor') === 0) {
|
} else if (port.name && port.name.indexOf('monitor') === 0) {
|
||||||
id = getId(port.sender, port.name);
|
id = getId(port.sender!, port.name);
|
||||||
connections.monitor[id] = port;
|
connections.monitor[id] = port;
|
||||||
monitorInstances(true);
|
monitorInstances(true);
|
||||||
monitors++;
|
monitors++;
|
||||||
port.onDisconnect.addListener(disconnect('monitor', id));
|
port.onDisconnect.addListener(disconnect('monitor', id));
|
||||||
} else {
|
} else {
|
||||||
// devpanel
|
// devpanel
|
||||||
id = port.name || port.sender.frameId;
|
id = port.name || port.sender!.frameId!;
|
||||||
connections.panel[id] = port;
|
connections.panel[id] = port;
|
||||||
monitorInstances(true, port.name);
|
monitorInstances(true, port.name);
|
||||||
monitors++;
|
monitors++;
|
||||||
listener = (msg) => {
|
listener = (msg: StoreAction) => {
|
||||||
window.store.dispatch(msg);
|
window.store.dispatch(msg);
|
||||||
};
|
};
|
||||||
port.onMessage.addListener(listener);
|
port.onMessage.addListener(listener);
|
||||||
|
@ -253,10 +312,16 @@ chrome.notifications.onClicked.addListener((id) => {
|
||||||
openDevToolsWindow('devtools-right');
|
openDevToolsWindow('devtools-right');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
syncOptions: SyncOptions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
||||||
|
|
||||||
export default function api() {
|
export default function api() {
|
||||||
return (next) => (action) => {
|
return (next: Dispatch<StoreAction>) => (action: StoreAction) => {
|
||||||
if (action.type === LIFTED_ACTION) toContentScript(action);
|
if (action.type === LIFTED_ACTION) toContentScript(action);
|
||||||
else if (action.type === 'TOGGLE_PERSIST') togglePersist();
|
else if (action.type === 'TOGGLE_PERSIST') togglePersist();
|
||||||
return next(action);
|
return next(action);
|
|
@ -1,5 +1,26 @@
|
||||||
export default class Monitor {
|
import { Action } from 'redux';
|
||||||
constructor(update) {
|
import { LiftedState } from '@redux-devtools/instrument';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__REDUX_DEVTOOLS_EXTENSION_LOCKED__?: boolean;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Monitor<S, A extends Action<unknown>> {
|
||||||
|
update: (
|
||||||
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
|
libConfig?: unknown
|
||||||
|
) => void;
|
||||||
|
active?: boolean;
|
||||||
|
paused?: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
update: (
|
||||||
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
|
libConfig?: unknown
|
||||||
|
) => void
|
||||||
|
) {
|
||||||
this.update = update;
|
this.update = update;
|
||||||
}
|
}
|
||||||
reducer = (state = {}, action) => {
|
reducer = (state = {}, action) => {
|
||||||
|
@ -15,7 +36,7 @@ export default class Monitor {
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
};
|
};
|
||||||
start = (skipUpdate) => {
|
start = (skipUpdate: boolean) => {
|
||||||
this.active = true;
|
this.active = true;
|
||||||
if (!skipUpdate) this.update();
|
if (!skipUpdate) this.update();
|
||||||
};
|
};
|
|
@ -3,7 +3,7 @@ import rootReducer, { BackgroundState } from '../reducers/background';
|
||||||
import api from '../middlewares/api';
|
import api from '../middlewares/api';
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
preloadedState: PreloadedState<BackgroundState>
|
preloadedState?: PreloadedState<BackgroundState>
|
||||||
) {
|
) {
|
||||||
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
return createStore(rootReducer, preloadedState, applyMiddleware(api));
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
import { createStore } from 'redux';
|
|
||||||
|
|
||||||
export default function configureStore(reducer, initialState, enhance) {
|
|
||||||
return createStore(reducer, initialState, enhance());
|
|
||||||
}
|
|
15
extension/src/app/stores/createStore.ts
Normal file
15
extension/src/app/stores/createStore.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import {
|
||||||
|
Action,
|
||||||
|
createStore,
|
||||||
|
PreloadedState,
|
||||||
|
Reducer,
|
||||||
|
StoreEnhancer,
|
||||||
|
} from 'redux';
|
||||||
|
|
||||||
|
export default function configureStore<S, A extends Action<unknown>>(
|
||||||
|
reducer: Reducer<S, A>,
|
||||||
|
initialState: PreloadedState<S> | undefined,
|
||||||
|
enhance: () => StoreEnhancer
|
||||||
|
) {
|
||||||
|
return createStore(reducer, initialState, enhance());
|
||||||
|
}
|
|
@ -1,15 +1,26 @@
|
||||||
import { compose } from 'redux';
|
import { Action, compose, Reducer, StoreEnhancerStoreCreator } from 'redux';
|
||||||
import instrument from '@redux-devtools/instrument';
|
import instrument, {
|
||||||
|
LiftedAction,
|
||||||
|
LiftedState,
|
||||||
|
} from '@redux-devtools/instrument';
|
||||||
import persistState from '@redux-devtools/core/lib/persistState';
|
import persistState from '@redux-devtools/core/lib/persistState';
|
||||||
|
import {
|
||||||
|
Config,
|
||||||
|
ConfigWithExpandedMaxAge,
|
||||||
|
} from '../../browser/extension/inject/pageScript';
|
||||||
|
|
||||||
export function getUrlParam(key) {
|
export function getUrlParam(key: string) {
|
||||||
const matches = window.location.href.match(
|
const matches = window.location.href.match(
|
||||||
new RegExp(`[?&]${key}=([^&#]+)\\b`)
|
new RegExp(`[?&]${key}=([^&#]+)\\b`)
|
||||||
);
|
);
|
||||||
return matches && matches.length > 0 ? matches[1] : null;
|
return matches && matches.length > 0 ? matches[1] : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function configureStore(next, monitorReducer, config) {
|
export default function configureStore(
|
||||||
|
next: StoreEnhancerStoreCreator,
|
||||||
|
monitorReducer: Reducer,
|
||||||
|
config: ConfigWithExpandedMaxAge
|
||||||
|
) {
|
||||||
return compose(
|
return compose(
|
||||||
instrument(monitorReducer, {
|
instrument(monitorReducer, {
|
||||||
maxAge: config.maxAge,
|
maxAge: config.maxAge,
|
|
@ -1,7 +1,16 @@
|
||||||
|
import { Store } from 'redux';
|
||||||
|
import { StoreAction } from '@redux-devtools/app/lib/actions';
|
||||||
import configureStore from '../../../app/stores/backgroundStore';
|
import configureStore from '../../../app/stores/backgroundStore';
|
||||||
import openDevToolsWindow from './openWindow';
|
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||||
import { createMenu, removeMenu } from './contextMenus';
|
import { createMenu, removeMenu } from './contextMenus';
|
||||||
import syncOptions from '../options/syncOptions';
|
import syncOptions from '../options/syncOptions';
|
||||||
|
import { BackgroundState } from '../../../app/reducers/background';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
store: Store<BackgroundState, StoreAction>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Expose the extension's store globally to access it from the windows
|
// Expose the extension's store globally to access it from the windows
|
||||||
// via chrome.runtime.getBackgroundPage
|
// via chrome.runtime.getBackgroundPage
|
||||||
|
@ -9,7 +18,7 @@ window.store = configureStore();
|
||||||
|
|
||||||
// Listen for keyboard shortcuts
|
// Listen for keyboard shortcuts
|
||||||
chrome.commands.onCommand.addListener((shortcut) => {
|
chrome.commands.onCommand.addListener((shortcut) => {
|
||||||
openDevToolsWindow(shortcut);
|
openDevToolsWindow(shortcut as DevToolsPosition);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Create the context menu when installed
|
// Create the context menu when installed
|
||||||
|
|
|
@ -8,7 +8,13 @@ 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
|
||||||
const maxChromeMsgSize = 32 * 1024 * 1024;
|
const maxChromeMsgSize = 32 * 1024 * 1024;
|
||||||
let connected = false;
|
let connected = false;
|
||||||
let bg;
|
let bg: chrome.runtime.Port | undefined;
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
devToolsExtensionID?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function connect() {
|
function connect() {
|
||||||
// Connect to the background script
|
// Connect to the background script
|
||||||
|
@ -57,7 +63,10 @@ function handleDisconnect() {
|
||||||
bg = undefined;
|
bg = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryCatch(fn, args) {
|
function tryCatch<A>(
|
||||||
|
fn: (args: PageScriptToContentScriptMessage) => void,
|
||||||
|
args: PageScriptToContentScriptMessage
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
return fn(args);
|
return fn(args);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -100,18 +109,51 @@ function tryCatch(fn, args) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(message) {
|
interface InitInstancePageScriptToContentScriptMessage {
|
||||||
|
readonly type: 'INIT_INSTANCE';
|
||||||
|
readonly instanceId: number;
|
||||||
|
readonly source: typeof pageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DisconnectMessage {
|
||||||
|
readonly type: 'DISCONNECT';
|
||||||
|
readonly source: typeof pageSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type PageScriptToContentScriptMessage =
|
||||||
|
| InitInstancePageScriptToContentScriptMessage
|
||||||
|
| DisconnectMessage;
|
||||||
|
|
||||||
|
interface InitInstanceContentScriptToBackgroundMessage {
|
||||||
|
readonly name: 'INIT_INSTANCE';
|
||||||
|
readonly instanceId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RelayMessage {
|
||||||
|
readonly name: 'RELAY';
|
||||||
|
readonly message: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ContentScriptToBackgroundMessage =
|
||||||
|
| InitInstanceContentScriptToBackgroundMessage
|
||||||
|
| RelayMessage;
|
||||||
|
|
||||||
|
function postToBackground(message: ContentScriptToBackgroundMessage) {
|
||||||
|
bg!.postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(message: never) {
|
||||||
if (!connected) connect();
|
if (!connected) connect();
|
||||||
if (message.type === 'INIT_INSTANCE') {
|
if (message.type === 'INIT_INSTANCE') {
|
||||||
getOptionsFromBg();
|
getOptionsFromBg();
|
||||||
bg.postMessage({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||||
} else {
|
} else {
|
||||||
bg.postMessage({ name: 'RELAY', message });
|
postToBackground({ name: 'RELAY', message });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resend messages from the page to the background script
|
// Resend messages from the page to the background script
|
||||||
function handleMessages(event) {
|
function handleMessages(event: MessageEvent<PageScriptToContentScriptMessage>) {
|
||||||
if (!isAllowed()) return;
|
if (!isAllowed()) return;
|
||||||
if (!event || event.source !== window || typeof event.data !== 'object') {
|
if (!event || event.source !== window || typeof event.data !== 'object') {
|
||||||
return;
|
return;
|
|
@ -1,8 +1,10 @@
|
||||||
import { getActionsArray, evalAction } from '@redux-devtools/utils';
|
import { getActionsArray, evalAction } from '@redux-devtools/utils';
|
||||||
import throttle from 'lodash/throttle';
|
import throttle from 'lodash/throttle';
|
||||||
|
import { Action, PreloadedState, Reducer, Store, StoreEnhancer } from 'redux';
|
||||||
|
import Immutable from 'immutable';
|
||||||
import createStore from '../../../app/stores/createStore';
|
import createStore from '../../../app/stores/createStore';
|
||||||
import configureStore, { getUrlParam } from '../../../app/stores/enhancerStore';
|
import configureStore, { getUrlParam } from '../../../app/stores/enhancerStore';
|
||||||
import { isAllowed } from '../options/syncOptions';
|
import { isAllowed, Options } from '../options/syncOptions';
|
||||||
import Monitor from '../../../app/service/Monitor';
|
import Monitor from '../../../app/service/Monitor';
|
||||||
import {
|
import {
|
||||||
noFiltersApplied,
|
noFiltersApplied,
|
||||||
|
@ -13,7 +15,7 @@ import {
|
||||||
} from '../../../app/api/filters';
|
} from '../../../app/api/filters';
|
||||||
import notifyErrors from '../../../app/api/notifyErrors';
|
import notifyErrors from '../../../app/api/notifyErrors';
|
||||||
import importState from '../../../app/api/importState';
|
import importState from '../../../app/api/importState';
|
||||||
import openWindow from '../../../app/api/openWindow';
|
import openWindow, { Position } from '../../../app/api/openWindow';
|
||||||
import generateId from '../../../app/api/generateInstanceId';
|
import generateId from '../../../app/api/generateInstanceId';
|
||||||
import {
|
import {
|
||||||
updateStore,
|
updateStore,
|
||||||
|
@ -23,14 +25,21 @@ import {
|
||||||
connect,
|
connect,
|
||||||
disconnect,
|
disconnect,
|
||||||
isInIframe,
|
isInIframe,
|
||||||
getSeralizeParameter,
|
getSerializeParameter,
|
||||||
|
Serialize,
|
||||||
} from '../../../app/api';
|
} from '../../../app/api';
|
||||||
|
import {
|
||||||
|
InstrumentExt,
|
||||||
|
LiftedAction,
|
||||||
|
LiftedState,
|
||||||
|
PerformAction,
|
||||||
|
} from '@redux-devtools/instrument';
|
||||||
|
|
||||||
const source = '@devtools-page';
|
const source = '@devtools-page';
|
||||||
let stores = {};
|
let stores: { [instanceId: number]: Store<unknown, Action<unknown>> } = {};
|
||||||
let reportId;
|
let reportId: string | null | undefined;
|
||||||
|
|
||||||
function deprecateParam(oldParam, newParam) {
|
function deprecateParam(oldParam: string, newParam: string) {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.warn(
|
console.warn(
|
||||||
`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md`
|
`${oldParam} parameter is deprecated, use ${newParam} instead: https://github.com/zalmoxisus/redux-devtools-extension/blob/master/docs/API/Arguments.md`
|
||||||
|
@ -38,10 +47,96 @@ function deprecateParam(oldParam, newParam) {
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
}
|
}
|
||||||
|
|
||||||
const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
interface SerializeWithImmutable extends Serialize {
|
||||||
reducer,
|
readonly immutable?: typeof Immutable;
|
||||||
preloadedState,
|
readonly refs?: (new (data: any) => unknown)[] | null;
|
||||||
config
|
}
|
||||||
|
|
||||||
|
export interface ConfigWithExpandedMaxAge {
|
||||||
|
readonly instanceId?: number;
|
||||||
|
readonly actionsBlacklist?: string | readonly string[];
|
||||||
|
readonly actionsWhitelist?: string | readonly string[];
|
||||||
|
readonly serialize?: boolean | SerializeWithImmutable;
|
||||||
|
readonly serializeState?:
|
||||||
|
| boolean
|
||||||
|
| ((key: string, value: unknown) => unknown)
|
||||||
|
| Serialize;
|
||||||
|
readonly serializeAction?:
|
||||||
|
| boolean
|
||||||
|
| ((key: string, value: unknown) => unknown)
|
||||||
|
| Serialize;
|
||||||
|
readonly statesFilter?: <S>(state: S, index: number) => S;
|
||||||
|
readonly actionsFilter?: <A extends Action<unknown>>(
|
||||||
|
action: A,
|
||||||
|
id: number
|
||||||
|
) => A;
|
||||||
|
readonly stateSanitizer?: <S>(state: S, index: number) => S;
|
||||||
|
readonly actionSanitizer?: <A extends Action<unknown>>(
|
||||||
|
action: A,
|
||||||
|
id: number
|
||||||
|
) => A;
|
||||||
|
readonly predicate?: <S, A extends Action<unknown>>(
|
||||||
|
state: S,
|
||||||
|
action: A
|
||||||
|
) => boolean;
|
||||||
|
readonly latency?: number;
|
||||||
|
readonly getMonitor?: <S, A extends Action<unknown>>(
|
||||||
|
monitor: Monitor<S, A>
|
||||||
|
) => void;
|
||||||
|
readonly maxAge?:
|
||||||
|
| number
|
||||||
|
| (<S, A extends Action<unknown>>(
|
||||||
|
currentLiftedAction: LiftedAction<S, A, unknown>,
|
||||||
|
previousLiftedState: LiftedState<S, A, unknown> | undefined
|
||||||
|
) => number);
|
||||||
|
readonly trace?:
|
||||||
|
| boolean
|
||||||
|
| (<A extends Action<unknown>>(action: A) => string | undefined);
|
||||||
|
readonly traceLimit?: number;
|
||||||
|
readonly shouldCatchErrors?: boolean;
|
||||||
|
readonly shouldHotReload?: boolean;
|
||||||
|
readonly shouldRecordChanges?: boolean;
|
||||||
|
readonly shouldStartLocked?: boolean;
|
||||||
|
readonly pauseActionType?: unknown;
|
||||||
|
readonly deserializeState?: <S>(state: S) => S;
|
||||||
|
readonly deserializeAction?: <A extends Action<unknown>>(action: A) => A;
|
||||||
|
readonly name?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Config extends ConfigWithExpandedMaxAge {
|
||||||
|
readonly maxAge?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ReduxDevtoolsExtension {
|
||||||
|
<S, A extends Action<unknown>>(
|
||||||
|
reducer: Reducer<S, A>,
|
||||||
|
preloadedState?: PreloadedState<S>,
|
||||||
|
config?: Config
|
||||||
|
): Store<S, A>;
|
||||||
|
(config: Config): StoreEnhancer;
|
||||||
|
open: (position?: Position) => void;
|
||||||
|
notifyErrors: (onError: () => boolean) => void;
|
||||||
|
disconnect: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
devToolsOptions: Options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const __REDUX_DEVTOOLS_EXTENSION__ = reduxDevtoolsExtension;
|
||||||
|
|
||||||
|
function reduxDevtoolsExtension<S, A extends Action<unknown>>(
|
||||||
|
reducer?: Reducer<S, A>,
|
||||||
|
preloadedState?: PreloadedState<S>,
|
||||||
|
config?: Config
|
||||||
|
): Store<S, A>;
|
||||||
|
function reduxDevtoolsExtension(config: Config): StoreEnhancer;
|
||||||
|
function reduxDevtoolsExtension<S, A extends Action<unknown>>(
|
||||||
|
reducer?: Reducer<S, A> | Config | undefined,
|
||||||
|
preloadedState?: PreloadedState<S>,
|
||||||
|
config?: Config
|
||||||
) {
|
) {
|
||||||
/* eslint-disable no-param-reassign */
|
/* eslint-disable no-param-reassign */
|
||||||
if (typeof reducer === 'object') {
|
if (typeof reducer === 'object') {
|
||||||
|
@ -51,15 +146,15 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
/* eslint-enable no-param-reassign */
|
/* eslint-enable no-param-reassign */
|
||||||
if (!window.devToolsOptions) window.devToolsOptions = {};
|
if (!window.devToolsOptions) window.devToolsOptions = {};
|
||||||
|
|
||||||
let store;
|
let store: Store<S, A> & InstrumentExt<S, A, unknown>;
|
||||||
let errorOccurred = false;
|
let errorOccurred = false;
|
||||||
let maxAge;
|
let maxAge: number | undefined;
|
||||||
let actionCreators;
|
let actionCreators;
|
||||||
let sendingActionId = 1;
|
let sendingActionId = 1;
|
||||||
const instanceId = generateId(config.instanceId);
|
const instanceId = generateId(config.instanceId);
|
||||||
const localFilter = getLocalFilter(config);
|
const localFilter = getLocalFilter(config);
|
||||||
const serializeState = getSeralizeParameter(config, 'serializeState');
|
const serializeState = getSerializeParameter(config, 'serializeState');
|
||||||
const serializeAction = getSeralizeParameter(config, 'serializeAction');
|
const serializeAction = getSerializeParameter(config, 'serializeAction');
|
||||||
let {
|
let {
|
||||||
statesFilter,
|
statesFilter,
|
||||||
actionsFilter,
|
actionsFilter,
|
||||||
|
@ -79,6 +174,19 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
actionSanitizer = actionsFilter; // eslint-disable-line no-param-reassign
|
actionSanitizer = actionsFilter; // eslint-disable-line no-param-reassign
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const relayState = throttle(
|
||||||
|
(
|
||||||
|
liftedState?: LiftedState<S, A, unknown> | undefined,
|
||||||
|
libConfig?: unknown
|
||||||
|
) => {
|
||||||
|
relayAction.cancel();
|
||||||
|
const state = liftedState || store.liftedStore.getState();
|
||||||
|
sendingActionId = state.nextActionId;
|
||||||
|
relay('STATE', state, undefined, undefined, libConfig);
|
||||||
|
},
|
||||||
|
latency
|
||||||
|
);
|
||||||
|
|
||||||
const monitor = new Monitor(relayState);
|
const monitor = new Monitor(relayState);
|
||||||
if (config.getMonitor) {
|
if (config.getMonitor) {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
@ -95,7 +203,7 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
function exportState() {
|
function exportState() {
|
||||||
const liftedState = store.liftedStore.getState();
|
const liftedState = store.liftedStore.getState();
|
||||||
const actionsById = liftedState.actionsById;
|
const actionsById = liftedState.actionsById;
|
||||||
const payload = [];
|
const payload: A[] = [];
|
||||||
liftedState.stagedActionIds.slice(1).forEach((id) => {
|
liftedState.stagedActionIds.slice(1).forEach((id) => {
|
||||||
// if (isFiltered(actionsById[id].action, localFilter)) return;
|
// if (isFiltered(actionsById[id].action, localFilter)) return;
|
||||||
payload.push(actionsById[id].action);
|
payload.push(actionsById[id].action);
|
||||||
|
@ -113,7 +221,30 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function relay(type, state, action, nextActionId, libConfig) {
|
function relay(
|
||||||
|
type: 'ACTION',
|
||||||
|
state: S,
|
||||||
|
action: PerformAction<A>,
|
||||||
|
nextActionId: number
|
||||||
|
): void;
|
||||||
|
function relay(
|
||||||
|
type: 'STATE',
|
||||||
|
state: LiftedState<S, A, unknown>,
|
||||||
|
action?: undefined,
|
||||||
|
nextActionId?: undefined,
|
||||||
|
libConfig?: unknown
|
||||||
|
): void;
|
||||||
|
function relay(type: 'ERROR', message: unknown): void;
|
||||||
|
function relay(type: 'INIT_INSTANCE'): void;
|
||||||
|
function relay(type: 'GET_REPORT', reportId: string): void;
|
||||||
|
function relay(type: 'STOP'): void;
|
||||||
|
function relay(
|
||||||
|
type: string,
|
||||||
|
state?: S | LiftedState<S, A, unknown> | unknown,
|
||||||
|
action?: PerformAction<A> | undefined,
|
||||||
|
nextActionId?: number | undefined,
|
||||||
|
libConfig?: unknown
|
||||||
|
) {
|
||||||
const message = {
|
const message = {
|
||||||
type,
|
type,
|
||||||
payload: filterState(
|
payload: filterState(
|
||||||
|
@ -142,13 +273,6 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
toContentScript(message, serializeState, serializeAction);
|
toContentScript(message, serializeState, serializeAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
const relayState = throttle((liftedState, libConfig) => {
|
|
||||||
relayAction.cancel();
|
|
||||||
const state = liftedState || store.liftedStore.getState();
|
|
||||||
sendingActionId = state.nextActionId;
|
|
||||||
relay('STATE', state, undefined, undefined, libConfig);
|
|
||||||
}, latency);
|
|
||||||
|
|
||||||
const relayAction = throttle(() => {
|
const relayAction = throttle(() => {
|
||||||
const liftedState = store.liftedStore.getState();
|
const liftedState = store.liftedStore.getState();
|
||||||
const nextActionId = liftedState.nextActionId;
|
const nextActionId = liftedState.nextActionId;
|
||||||
|
@ -296,8 +420,11 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const filteredActionIds = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length
|
const filteredActionIds: number[] = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length
|
||||||
const getMaxAge = (liftedAction, liftedState) => {
|
const getMaxAge = (
|
||||||
|
liftedAction?: LiftedAction<S, A, unknown>,
|
||||||
|
liftedState?: LiftedState<S, A, unknown> | undefined
|
||||||
|
) => {
|
||||||
let m = (config && config.maxAge) || window.devToolsOptions.maxAge || 50;
|
let m = (config && config.maxAge) || window.devToolsOptions.maxAge || 50;
|
||||||
if (
|
if (
|
||||||
!liftedAction ||
|
!liftedAction ||
|
||||||
|
@ -311,9 +438,9 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
// TODO: check also predicate && !predicate(state, action) with current state
|
// TODO: check also predicate && !predicate(state, action) with current state
|
||||||
maxAge++;
|
maxAge++;
|
||||||
} else {
|
} else {
|
||||||
filteredActionIds.push(liftedState.nextActionId);
|
filteredActionIds.push(liftedState!.nextActionId);
|
||||||
if (filteredActionIds.length >= m) {
|
if (filteredActionIds.length >= m) {
|
||||||
const stagedActionIds = liftedState.stagedActionIds;
|
const stagedActionIds = liftedState!.stagedActionIds;
|
||||||
let i = 1;
|
let i = 1;
|
||||||
while (
|
while (
|
||||||
maxAge > m &&
|
maxAge > m &&
|
||||||
|
@ -367,16 +494,16 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
relayState(liftedState);
|
relayState(liftedState);
|
||||||
}
|
}
|
||||||
|
|
||||||
const enhance = () => (next) => {
|
const enhance = (): StoreEnhancer => (next) => {
|
||||||
return (reducer_, initialState_, enhancer_) => {
|
return (reducer_, initialState_) => {
|
||||||
if (!isAllowed(window.devToolsOptions)) {
|
if (!isAllowed(window.devToolsOptions)) {
|
||||||
return next(reducer_, initialState_, enhancer_);
|
return next(reducer_, initialState_);
|
||||||
}
|
}
|
||||||
|
|
||||||
store = stores[instanceId] = configureStore(next, monitor.reducer, {
|
store = stores[instanceId] = configureStore(next, monitor.reducer, {
|
||||||
...config,
|
...config,
|
||||||
maxAge: getMaxAge,
|
maxAge: getMaxAge,
|
||||||
})(reducer_, initialState_, enhancer_);
|
})(reducer_, initialState_);
|
||||||
|
|
||||||
if (isInIframe()) setTimeout(init, 3000);
|
if (isInIframe()) setTimeout(init, 3000);
|
||||||
else init();
|
else init();
|
||||||
|
@ -392,10 +519,16 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||||
);
|
);
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
return createStore(reducer, preloadedState, enhance);
|
return createStore(reducer, preloadedState, enhance);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__REDUX_DEVTOOLS_EXTENSION__: ReduxDevtoolsExtension;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// noinspection JSAnnotator
|
// noinspection JSAnnotator
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__ = __REDUX_DEVTOOLS_EXTENSION__;
|
window.__REDUX_DEVTOOLS_EXTENSION__ = __REDUX_DEVTOOLS_EXTENSION__ as any;
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.open = openWindow;
|
window.__REDUX_DEVTOOLS_EXTENSION__.open = openWindow;
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.updateStore = updateStore(stores);
|
window.__REDUX_DEVTOOLS_EXTENSION__.updateStore = updateStore(stores);
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors = notifyErrors;
|
window.__REDUX_DEVTOOLS_EXTENSION__.notifyErrors = notifyErrors;
|
||||||
|
@ -404,50 +537,6 @@ window.__REDUX_DEVTOOLS_EXTENSION__.listen = setListener;
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.connect = connect;
|
window.__REDUX_DEVTOOLS_EXTENSION__.connect = connect;
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION__.disconnect = disconnect;
|
window.__REDUX_DEVTOOLS_EXTENSION__.disconnect = disconnect;
|
||||||
|
|
||||||
// Deprecated
|
|
||||||
/* eslint-disable no-console */
|
|
||||||
let varNameDeprecatedWarned;
|
|
||||||
const varNameDeprecatedWarn = () => {
|
|
||||||
if (varNameDeprecatedWarned) return;
|
|
||||||
console.warn(
|
|
||||||
'`window.devToolsExtension` is deprecated in favor of `window.__REDUX_DEVTOOLS_EXTENSION__`, and will be removed in next version of Redux DevTools: https://git.io/fpEJZ'
|
|
||||||
);
|
|
||||||
varNameDeprecatedWarned = true;
|
|
||||||
};
|
|
||||||
/* eslint-enable no-console */
|
|
||||||
window.devToolsExtension = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return __REDUX_DEVTOOLS_EXTENSION__.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.open = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return openWindow.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.updateStore = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return updateStore(stores).apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.notifyErrors = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return notifyErrors.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.send = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return sendMessage.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.listen = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return setListener.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.connect = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return connect.apply(null, args);
|
|
||||||
};
|
|
||||||
window.devToolsExtension.disconnect = (...args) => {
|
|
||||||
varNameDeprecatedWarn();
|
|
||||||
return disconnect.apply(null, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const preEnhancer =
|
const preEnhancer =
|
||||||
(instanceId) => (next) => (reducer, preloadedState, enhancer) => {
|
(instanceId) => (next) => (reducer, preloadedState, enhancer) => {
|
||||||
const store = next(reducer, preloadedState, enhancer);
|
const store = next(reducer, preloadedState, enhancer);
|
||||||
|
@ -464,8 +553,8 @@ const preEnhancer =
|
||||||
};
|
};
|
||||||
|
|
||||||
const extensionCompose =
|
const extensionCompose =
|
||||||
(config) =>
|
(config: Config) =>
|
||||||
(...funcs) => {
|
(...funcs: StoreEnhancer[]) => {
|
||||||
return (...args) => {
|
return (...args) => {
|
||||||
const instanceId = generateId(config.instanceId);
|
const instanceId = generateId(config.instanceId);
|
||||||
return [preEnhancer(instanceId), ...funcs].reduceRight(
|
return [preEnhancer(instanceId), ...funcs].reduceRight(
|
||||||
|
@ -475,6 +564,12 @@ const extensionCompose =
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = (...funcs) => {
|
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = (...funcs) => {
|
||||||
if (funcs.length === 0) {
|
if (funcs.length === 0) {
|
||||||
return __REDUX_DEVTOOLS_EXTENSION__();
|
return __REDUX_DEVTOOLS_EXTENSION__();
|
|
@ -6,7 +6,7 @@ import { Options } from './syncOptions';
|
||||||
import '../../views/options.pug';
|
import '../../views/options.pug';
|
||||||
|
|
||||||
chrome.runtime.getBackgroundPage((background) => {
|
chrome.runtime.getBackgroundPage((background) => {
|
||||||
const syncOptions = background.syncOptions;
|
const syncOptions = background!.syncOptions;
|
||||||
|
|
||||||
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
||||||
syncOptions.save(name, value);
|
syncOptions.save(name, value);
|
||||||
|
|
|
@ -31,7 +31,11 @@ interface OldOrNewOptions {
|
||||||
let options: Options | undefined;
|
let options: Options | undefined;
|
||||||
let subscribers: ((options: Options) => void)[] = [];
|
let subscribers: ((options: Options) => void)[] = [];
|
||||||
|
|
||||||
type ToAllTabs = (msg: { readonly options: Options }) => void;
|
export interface OptionsMessage {
|
||||||
|
readonly options: Options;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToAllTabs = (msg: OptionsMessage) => void;
|
||||||
|
|
||||||
const save =
|
const save =
|
||||||
(toAllTabs: ToAllTabs | undefined) =>
|
(toAllTabs: ToAllTabs | undefined) =>
|
||||||
|
@ -132,7 +136,13 @@ export const isAllowed = (localOptions = options) =>
|
||||||
!localOptions.urls ||
|
!localOptions.urls ||
|
||||||
location.href.match(toReg(localOptions.urls)!);
|
location.href.match(toReg(localOptions.urls)!);
|
||||||
|
|
||||||
export default function syncOptions(toAllTabs?: ToAllTabs) {
|
export interface SyncOptions {
|
||||||
|
readonly save: <K extends keyof Options>(key: K, value: Options[K]) => void;
|
||||||
|
readonly get: (callback: (options: Options) => void) => void;
|
||||||
|
readonly subscribe: (callback: (options: Options) => void) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function syncOptions(toAllTabs?: ToAllTabs): SyncOptions {
|
||||||
if (toAllTabs && !options) get(() => {}); // Initialize
|
if (toAllTabs && !options) get(() => {}); // Initialize
|
||||||
return {
|
return {
|
||||||
save: save(toAllTabs),
|
save: save(toAllTabs),
|
||||||
|
|
|
@ -16,7 +16,8 @@ getPreloadedState(position, (state) => {
|
||||||
preloadedState = state;
|
preloadedState = state;
|
||||||
});
|
});
|
||||||
|
|
||||||
chrome.runtime.getBackgroundPage(({ store }) => {
|
chrome.runtime.getBackgroundPage((window) => {
|
||||||
|
const { store } = window!;
|
||||||
const localStore = configureStore(store, position, preloadedState);
|
const localStore = configureStore(store, position, preloadedState);
|
||||||
let name = 'monitor';
|
let name = 'monitor';
|
||||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user