mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2025-07-27 00:19:55 +03:00
stash
This commit is contained in:
parent
7b4698225b
commit
a3df1df2eb
|
@ -1,4 +1,7 @@
|
|||
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 =
|
||||
| 'DO_NOT_FILTER'
|
||||
|
@ -11,13 +14,22 @@ export const FilterState: { [K in FilterStateValue]: FilterStateValue } = {
|
|||
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) {
|
||||
return {
|
||||
whitelist: Array.isArray(config.actionsWhitelist)
|
||||
whitelist: isArray(config.actionsWhitelist)
|
||||
? config.actionsWhitelist.join('|')
|
||||
: config.actionsWhitelist,
|
||||
blacklist: Array.isArray(config.actionsBlacklist)
|
||||
blacklist: isArray(config.actionsBlacklist)
|
||||
? config.actionsBlacklist.join('|')
|
||||
: config.actionsBlacklist,
|
||||
};
|
||||
|
@ -125,13 +137,17 @@ export function filterState(
|
|||
};
|
||||
}
|
||||
|
||||
export function startingFrom(
|
||||
sendingActionId,
|
||||
state,
|
||||
localFilter,
|
||||
stateSanitizer,
|
||||
actionSanitizer,
|
||||
predicate
|
||||
export function startingFrom<S, A extends Action<unknown>>(
|
||||
sendingActionId: number,
|
||||
state: LiftedState<S, A, unknown>,
|
||||
localFilter: LocalFilter | undefined,
|
||||
stateSanitizer: (<S>(state: S, index: number) => S) | undefined,
|
||||
actionSanitizer:
|
||||
| (<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;
|
||||
if (sendingActionId <= stagedActionIds[1]) return state;
|
||||
|
@ -142,7 +158,7 @@ export function startingFrom(
|
|||
const filteredStagedActionIds = shouldFilter ? [0] : stagedActionIds;
|
||||
const actionsById = state.actionsById;
|
||||
const computedStates = state.computedStates;
|
||||
const newActionsById = {};
|
||||
const newActionsById: { [key: number]: PerformAction<A> } = {};
|
||||
const newComputedStates = [];
|
||||
let key;
|
||||
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 seralizeImmutable from '@redux-devtools/serialize/lib/immutable/serialize';
|
||||
import serializeImmutable from '@redux-devtools/serialize/lib/immutable/serialize';
|
||||
import { getActionsArray } from '@redux-devtools/utils';
|
||||
import { getLocalFilter, isFiltered } from './filters';
|
||||
import importState from './importState';
|
||||
import generateId from './generateInstanceId';
|
||||
import { PageScriptToContentScriptMessage } from '../../browser/extension/inject/contentScript';
|
||||
import { Config } from '../../browser/extension/inject/pageScript';
|
||||
|
||||
const listeners = {};
|
||||
export const source = '@devtools-page';
|
||||
|
||||
function windowReplacer(key, value) {
|
||||
if (value && value.window === value) {
|
||||
function windowReplacer(key: string, value: unknown) {
|
||||
if (value && (value as Window).window === value) {
|
||||
return '[WINDOW]';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function tryCatchStringify(obj) {
|
||||
function tryCatchStringify(obj: unknown) {
|
||||
try {
|
||||
return JSON.stringify(obj);
|
||||
} catch (err) {
|
||||
|
@ -25,19 +27,19 @@ function tryCatchStringify(obj) {
|
|||
console.log('Failed to stringify', err);
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
return jsan.stringify(obj, windowReplacer, null, {
|
||||
return jsan.stringify(obj, windowReplacer, undefined, {
|
||||
circular: '[CIRCULAR]',
|
||||
date: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let stringifyWarned;
|
||||
function stringify(obj, serialize) {
|
||||
let stringifyWarned: boolean;
|
||||
function stringify(obj: unknown, serialize?: Serialize | undefined) {
|
||||
const str =
|
||||
typeof serialize === 'undefined'
|
||||
? 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) {
|
||||
// 16 MB
|
||||
|
@ -52,12 +54,21 @@ function stringify(obj, serialize) {
|
|||
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;
|
||||
if (serialize) {
|
||||
if (serialize === true) return { options: true };
|
||||
if (serialize.immutable) {
|
||||
const immutableSerializer = seralizeImmutable(
|
||||
const immutableSerializer = serializeImmutable(
|
||||
serialize.immutable,
|
||||
serialize.refs,
|
||||
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;
|
||||
// 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`
|
||||
);
|
||||
|
||||
if (typeof serializeState === 'boolean') return { options: value };
|
||||
if (typeof serializeState === 'function') return { replacer: value };
|
||||
if (typeof value === 'boolean') return { options: value };
|
||||
if (typeof value === 'function') return { replacer: value };
|
||||
return value;
|
||||
}
|
||||
|
||||
function post(message) {
|
||||
function post(message: PageScriptToContentScriptMessage) {
|
||||
window.postMessage(message, '*');
|
||||
}
|
||||
|
||||
function getStackTrace(config, toExcludeFromTrace) {
|
||||
function getStackTrace(config, toExcludeFromTrace: Function | undefined) {
|
||||
if (!config.trace) return undefined;
|
||||
if (typeof config.trace === 'function') return config.trace();
|
||||
|
||||
|
@ -123,7 +134,7 @@ function getStackTrace(config, toExcludeFromTrace) {
|
|||
typeof Error.stackTraceLimit !== 'number' ||
|
||||
Error.stackTraceLimit > traceLimit
|
||||
) {
|
||||
const frames = stack.split('\n');
|
||||
const frames = stack!.split('\n');
|
||||
if (frames.length > traceLimit) {
|
||||
stack = frames
|
||||
.slice(0, traceLimit + extraFrames + (frames[0] === 'Error' ? 1 : 0))
|
||||
|
@ -133,7 +144,11 @@ function getStackTrace(config, toExcludeFromTrace) {
|
|||
return stack;
|
||||
}
|
||||
|
||||
function amendActionType(action, config, toExcludeFromTrace) {
|
||||
function amendActionType(
|
||||
action,
|
||||
config,
|
||||
toExcludeFromTrace: Function | undefined
|
||||
) {
|
||||
let timestamp = Date.now();
|
||||
let stack = getStackTrace(config, toExcludeFromTrace);
|
||||
if (typeof action === 'string') {
|
||||
|
@ -144,7 +159,11 @@ function amendActionType(action, config, toExcludeFromTrace) {
|
|||
return { action, timestamp, stack };
|
||||
}
|
||||
|
||||
export function toContentScript(message, serializeState, serializeAction) {
|
||||
export function toContentScript(
|
||||
message,
|
||||
serializeState: Serialize | undefined,
|
||||
serializeAction: Serialize | undefined
|
||||
) {
|
||||
if (message.type === 'ACTION') {
|
||||
message.action = stringify(message.action, serializeAction);
|
||||
message.payload = stringify(message.payload, serializeState);
|
||||
|
@ -235,7 +254,7 @@ export function connect(preConfig) {
|
|||
config.name =
|
||||
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 latency = config.latency;
|
||||
const predicate = config.predicate;
|
||||
|
@ -245,7 +264,7 @@ export function connect(preConfig) {
|
|||
let delayedActions = [];
|
||||
let delayedStates = [];
|
||||
|
||||
const rootListiner = (action) => {
|
||||
const rootListener = (action) => {
|
||||
if (autoPause) {
|
||||
if (action.type === 'START') isPaused = false;
|
||||
else if (action.type === 'STOP') isPaused = true;
|
||||
|
@ -264,7 +283,7 @@ export function connect(preConfig) {
|
|||
}
|
||||
};
|
||||
|
||||
listeners[id] = [rootListiner];
|
||||
listeners[id] = [rootListener];
|
||||
|
||||
const subscribe = (listener) => {
|
||||
if (!listener) return undefined;
|
|
@ -1,9 +1,9 @@
|
|||
let handleError;
|
||||
let handleError: () => boolean;
|
||||
let lastTime = 0;
|
||||
|
||||
function createExpBackoffTimer(step) {
|
||||
function createExpBackoffTimer(step: number) {
|
||||
let count = 1;
|
||||
return function (reset) {
|
||||
return function (reset?: boolean) {
|
||||
// Reset call
|
||||
if (reset) {
|
||||
count = 1;
|
||||
|
@ -18,7 +18,7 @@ function createExpBackoffTimer(step) {
|
|||
|
||||
const nextErrorTimeout = createExpBackoffTimer(5000);
|
||||
|
||||
function postError(message) {
|
||||
function postError(message: string) {
|
||||
if (handleError && !handleError()) return;
|
||||
window.postMessage(
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ function postError(message) {
|
|||
);
|
||||
}
|
||||
|
||||
function catchErrors(e) {
|
||||
function catchErrors(e: ErrorEvent) {
|
||||
if (
|
||||
(window.devToolsOptions && !window.devToolsOptions.shouldCatchErrors) ||
|
||||
e.timeStamp - lastTime < nextErrorTimeout()
|
||||
|
@ -42,7 +42,7 @@ function catchErrors(e) {
|
|||
postError(e.message);
|
||||
}
|
||||
|
||||
export default function notifyErrors(onError) {
|
||||
export default function notifyErrors(onError: () => boolean) {
|
||||
handleError = onError;
|
||||
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(
|
||||
{
|
||||
source: '@devtools-page',
|
|
@ -5,13 +5,53 @@ import {
|
|||
LIFTED_ACTION,
|
||||
} from '@redux-devtools/app/lib/constants/actionTypes';
|
||||
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 { 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 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: {},
|
||||
panel: {},
|
||||
monitor: {},
|
||||
|
@ -20,10 +60,16 @@ const chunks = {};
|
|||
let monitors = 0;
|
||||
let isMonitored = false;
|
||||
|
||||
const getId = (sender, name) =>
|
||||
sender.tab ? sender.tab.id : name || sender.id;
|
||||
const getId = (sender: chrome.runtime.MessageSender, name?: string) =>
|
||||
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) => {
|
||||
connections.monitor[id].postMessage(
|
||||
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;
|
||||
Object.keys(tabs).forEach((id) => {
|
||||
tabs[id].postMessage(msg);
|
||||
});
|
||||
}
|
||||
|
||||
function monitorInstances(shouldMonitor, id) {
|
||||
function monitorInstances(shouldMonitor: boolean, id?: string) {
|
||||
if (!id && isMonitored === shouldMonitor) return;
|
||||
const action = { type: shouldMonitor ? 'START' : 'STOP' };
|
||||
const action = {
|
||||
type: shouldMonitor ? ('START' as const) : ('STOP' as const),
|
||||
};
|
||||
if (id) {
|
||||
if (connections.tab[id]) connections.tab[id].postMessage(action);
|
||||
} else {
|
||||
|
@ -80,8 +128,15 @@ function togglePersist() {
|
|||
}
|
||||
}
|
||||
|
||||
type BackgroundStoreMessage = unknown;
|
||||
type BackgroundStoreResponse = never;
|
||||
|
||||
// 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);
|
||||
if (!tabId) return;
|
||||
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() {
|
||||
const p = connections[type][id];
|
||||
if (listener && p) p.onMessage.removeListener(listener);
|
||||
|
@ -182,17 +241,17 @@ function disconnect(type, id, listener) {
|
|||
};
|
||||
}
|
||||
|
||||
function onConnect(port) {
|
||||
let id;
|
||||
function onConnect(port: chrome.runtime.Port) {
|
||||
let id: number | string;
|
||||
let listener;
|
||||
|
||||
window.store.dispatch({ type: CONNECTED, port });
|
||||
|
||||
if (port.name === 'tab') {
|
||||
id = getId(port.sender);
|
||||
if (port.sender.frameId) id = `${id}-${port.sender.frameId}`;
|
||||
id = getId(port.sender!);
|
||||
if (port.sender!.frameId) id = `${id}-${port.sender!.frameId}`;
|
||||
connections.tab[id] = port;
|
||||
listener = (msg) => {
|
||||
listener = (msg: TabToBackgroundMessage) => {
|
||||
if (msg.name === 'INIT_INSTANCE') {
|
||||
if (typeof id === 'number') {
|
||||
chrome.pageAction.show(id);
|
||||
|
@ -218,24 +277,24 @@ function onConnect(port) {
|
|||
return;
|
||||
}
|
||||
if (msg.name === 'RELAY') {
|
||||
messaging(msg.message, port.sender, id);
|
||||
messaging(msg.message, port.sender!, id);
|
||||
}
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
port.onDisconnect.addListener(disconnect('tab', id, listener));
|
||||
} 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;
|
||||
monitorInstances(true);
|
||||
monitors++;
|
||||
port.onDisconnect.addListener(disconnect('monitor', id));
|
||||
} else {
|
||||
// devpanel
|
||||
id = port.name || port.sender.frameId;
|
||||
id = port.name || port.sender!.frameId!;
|
||||
connections.panel[id] = port;
|
||||
monitorInstances(true, port.name);
|
||||
monitors++;
|
||||
listener = (msg) => {
|
||||
listener = (msg: StoreAction) => {
|
||||
window.store.dispatch(msg);
|
||||
};
|
||||
port.onMessage.addListener(listener);
|
||||
|
@ -253,10 +312,16 @@ chrome.notifications.onClicked.addListener((id) => {
|
|||
openDevToolsWindow('devtools-right');
|
||||
});
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
syncOptions: SyncOptions;
|
||||
}
|
||||
}
|
||||
|
||||
window.syncOptions = syncOptions(toAllTabs); // Expose to the options page
|
||||
|
||||
export default function api() {
|
||||
return (next) => (action) => {
|
||||
return (next: Dispatch<StoreAction>) => (action: StoreAction) => {
|
||||
if (action.type === LIFTED_ACTION) toContentScript(action);
|
||||
else if (action.type === 'TOGGLE_PERSIST') togglePersist();
|
||||
return next(action);
|
|
@ -1,5 +1,26 @@
|
|||
export default class Monitor {
|
||||
constructor(update) {
|
||||
import { Action } from 'redux';
|
||||
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;
|
||||
}
|
||||
reducer = (state = {}, action) => {
|
||||
|
@ -15,7 +36,7 @@ export default class Monitor {
|
|||
}
|
||||
return state;
|
||||
};
|
||||
start = (skipUpdate) => {
|
||||
start = (skipUpdate: boolean) => {
|
||||
this.active = true;
|
||||
if (!skipUpdate) this.update();
|
||||
};
|
|
@ -3,7 +3,7 @@ import rootReducer, { BackgroundState } from '../reducers/background';
|
|||
import api from '../middlewares/api';
|
||||
|
||||
export default function configureStore(
|
||||
preloadedState: PreloadedState<BackgroundState>
|
||||
preloadedState?: PreloadedState<BackgroundState>
|
||||
) {
|
||||
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 instrument from '@redux-devtools/instrument';
|
||||
import { Action, compose, Reducer, StoreEnhancerStoreCreator } from 'redux';
|
||||
import instrument, {
|
||||
LiftedAction,
|
||||
LiftedState,
|
||||
} from '@redux-devtools/instrument';
|
||||
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(
|
||||
new RegExp(`[?&]${key}=([^&#]+)\\b`)
|
||||
);
|
||||
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(
|
||||
instrument(monitorReducer, {
|
||||
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 openDevToolsWindow from './openWindow';
|
||||
import openDevToolsWindow, { DevToolsPosition } from './openWindow';
|
||||
import { createMenu, removeMenu } from './contextMenus';
|
||||
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
|
||||
// via chrome.runtime.getBackgroundPage
|
||||
|
@ -9,7 +18,7 @@ window.store = configureStore();
|
|||
|
||||
// Listen for keyboard shortcuts
|
||||
chrome.commands.onCommand.addListener((shortcut) => {
|
||||
openDevToolsWindow(shortcut);
|
||||
openDevToolsWindow(shortcut as DevToolsPosition);
|
||||
});
|
||||
|
||||
// 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
|
||||
const maxChromeMsgSize = 32 * 1024 * 1024;
|
||||
let connected = false;
|
||||
let bg;
|
||||
let bg: chrome.runtime.Port | undefined;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
devToolsExtensionID?: string;
|
||||
}
|
||||
}
|
||||
|
||||
function connect() {
|
||||
// Connect to the background script
|
||||
|
@ -57,7 +63,10 @@ function handleDisconnect() {
|
|||
bg = undefined;
|
||||
}
|
||||
|
||||
function tryCatch(fn, args) {
|
||||
function tryCatch<A>(
|
||||
fn: (args: PageScriptToContentScriptMessage) => void,
|
||||
args: PageScriptToContentScriptMessage
|
||||
) {
|
||||
try {
|
||||
return fn(args);
|
||||
} 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 (message.type === 'INIT_INSTANCE') {
|
||||
getOptionsFromBg();
|
||||
bg.postMessage({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||
postToBackground({ name: 'INIT_INSTANCE', instanceId: message.instanceId });
|
||||
} else {
|
||||
bg.postMessage({ name: 'RELAY', message });
|
||||
postToBackground({ name: 'RELAY', message });
|
||||
}
|
||||
}
|
||||
|
||||
// Resend messages from the page to the background script
|
||||
function handleMessages(event) {
|
||||
function handleMessages(event: MessageEvent<PageScriptToContentScriptMessage>) {
|
||||
if (!isAllowed()) return;
|
||||
if (!event || event.source !== window || typeof event.data !== 'object') {
|
||||
return;
|
|
@ -1,8 +1,10 @@
|
|||
import { getActionsArray, evalAction } from '@redux-devtools/utils';
|
||||
import throttle from 'lodash/throttle';
|
||||
import { Action, PreloadedState, Reducer, Store, StoreEnhancer } from 'redux';
|
||||
import Immutable from 'immutable';
|
||||
import createStore from '../../../app/stores/createStore';
|
||||
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 {
|
||||
noFiltersApplied,
|
||||
|
@ -13,7 +15,7 @@ import {
|
|||
} from '../../../app/api/filters';
|
||||
import notifyErrors from '../../../app/api/notifyErrors';
|
||||
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 {
|
||||
updateStore,
|
||||
|
@ -23,14 +25,21 @@ import {
|
|||
connect,
|
||||
disconnect,
|
||||
isInIframe,
|
||||
getSeralizeParameter,
|
||||
getSerializeParameter,
|
||||
Serialize,
|
||||
} from '../../../app/api';
|
||||
import {
|
||||
InstrumentExt,
|
||||
LiftedAction,
|
||||
LiftedState,
|
||||
PerformAction,
|
||||
} from '@redux-devtools/instrument';
|
||||
|
||||
const source = '@devtools-page';
|
||||
let stores = {};
|
||||
let reportId;
|
||||
let stores: { [instanceId: number]: Store<unknown, Action<unknown>> } = {};
|
||||
let reportId: string | null | undefined;
|
||||
|
||||
function deprecateParam(oldParam, newParam) {
|
||||
function deprecateParam(oldParam: string, newParam: string) {
|
||||
/* eslint-disable no-console */
|
||||
console.warn(
|
||||
`${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 */
|
||||
}
|
||||
|
||||
const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
||||
reducer,
|
||||
preloadedState,
|
||||
config
|
||||
interface SerializeWithImmutable extends Serialize {
|
||||
readonly immutable?: typeof Immutable;
|
||||
readonly refs?: (new (data: any) => unknown)[] | null;
|
||||
}
|
||||
|
||||
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 */
|
||||
if (typeof reducer === 'object') {
|
||||
|
@ -51,15 +146,15 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
/* eslint-enable no-param-reassign */
|
||||
if (!window.devToolsOptions) window.devToolsOptions = {};
|
||||
|
||||
let store;
|
||||
let store: Store<S, A> & InstrumentExt<S, A, unknown>;
|
||||
let errorOccurred = false;
|
||||
let maxAge;
|
||||
let maxAge: number | undefined;
|
||||
let actionCreators;
|
||||
let sendingActionId = 1;
|
||||
const instanceId = generateId(config.instanceId);
|
||||
const localFilter = getLocalFilter(config);
|
||||
const serializeState = getSeralizeParameter(config, 'serializeState');
|
||||
const serializeAction = getSeralizeParameter(config, 'serializeAction');
|
||||
const serializeState = getSerializeParameter(config, 'serializeState');
|
||||
const serializeAction = getSerializeParameter(config, 'serializeAction');
|
||||
let {
|
||||
statesFilter,
|
||||
actionsFilter,
|
||||
|
@ -79,6 +174,19 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
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);
|
||||
if (config.getMonitor) {
|
||||
/* eslint-disable no-console */
|
||||
|
@ -95,7 +203,7 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
function exportState() {
|
||||
const liftedState = store.liftedStore.getState();
|
||||
const actionsById = liftedState.actionsById;
|
||||
const payload = [];
|
||||
const payload: A[] = [];
|
||||
liftedState.stagedActionIds.slice(1).forEach((id) => {
|
||||
// if (isFiltered(actionsById[id].action, localFilter)) return;
|
||||
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 = {
|
||||
type,
|
||||
payload: filterState(
|
||||
|
@ -142,13 +273,6 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
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 liftedState = store.liftedStore.getState();
|
||||
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 getMaxAge = (liftedAction, liftedState) => {
|
||||
const filteredActionIds: number[] = []; // simple circular buffer of non-excluded actions with fixed maxAge-1 length
|
||||
const getMaxAge = (
|
||||
liftedAction?: LiftedAction<S, A, unknown>,
|
||||
liftedState?: LiftedState<S, A, unknown> | undefined
|
||||
) => {
|
||||
let m = (config && config.maxAge) || window.devToolsOptions.maxAge || 50;
|
||||
if (
|
||||
!liftedAction ||
|
||||
|
@ -311,9 +438,9 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
// TODO: check also predicate && !predicate(state, action) with current state
|
||||
maxAge++;
|
||||
} else {
|
||||
filteredActionIds.push(liftedState.nextActionId);
|
||||
filteredActionIds.push(liftedState!.nextActionId);
|
||||
if (filteredActionIds.length >= m) {
|
||||
const stagedActionIds = liftedState.stagedActionIds;
|
||||
const stagedActionIds = liftedState!.stagedActionIds;
|
||||
let i = 1;
|
||||
while (
|
||||
maxAge > m &&
|
||||
|
@ -367,16 +494,16 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
relayState(liftedState);
|
||||
}
|
||||
|
||||
const enhance = () => (next) => {
|
||||
return (reducer_, initialState_, enhancer_) => {
|
||||
const enhance = (): StoreEnhancer => (next) => {
|
||||
return (reducer_, initialState_) => {
|
||||
if (!isAllowed(window.devToolsOptions)) {
|
||||
return next(reducer_, initialState_, enhancer_);
|
||||
return next(reducer_, initialState_);
|
||||
}
|
||||
|
||||
store = stores[instanceId] = configureStore(next, monitor.reducer, {
|
||||
...config,
|
||||
maxAge: getMaxAge,
|
||||
})(reducer_, initialState_, enhancer_);
|
||||
})(reducer_, initialState_);
|
||||
|
||||
if (isInIframe()) setTimeout(init, 3000);
|
||||
else init();
|
||||
|
@ -392,10 +519,16 @@ const __REDUX_DEVTOOLS_EXTENSION__ = function (
|
|||
);
|
||||
/* eslint-enable no-console */
|
||||
return createStore(reducer, preloadedState, enhance);
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__REDUX_DEVTOOLS_EXTENSION__: ReduxDevtoolsExtension;
|
||||
}
|
||||
}
|
||||
|
||||
// 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__.updateStore = updateStore(stores);
|
||||
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__.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 =
|
||||
(instanceId) => (next) => (reducer, preloadedState, enhancer) => {
|
||||
const store = next(reducer, preloadedState, enhancer);
|
||||
|
@ -464,8 +553,8 @@ const preEnhancer =
|
|||
};
|
||||
|
||||
const extensionCompose =
|
||||
(config) =>
|
||||
(...funcs) => {
|
||||
(config: Config) =>
|
||||
(...funcs: StoreEnhancer[]) => {
|
||||
return (...args) => {
|
||||
const instanceId = generateId(config.instanceId);
|
||||
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) => {
|
||||
if (funcs.length === 0) {
|
||||
return __REDUX_DEVTOOLS_EXTENSION__();
|
|
@ -6,7 +6,7 @@ import { Options } from './syncOptions';
|
|||
import '../../views/options.pug';
|
||||
|
||||
chrome.runtime.getBackgroundPage((background) => {
|
||||
const syncOptions = background.syncOptions;
|
||||
const syncOptions = background!.syncOptions;
|
||||
|
||||
const saveOption = <K extends keyof Options>(name: K, value: Options[K]) => {
|
||||
syncOptions.save(name, value);
|
||||
|
|
|
@ -31,7 +31,11 @@ interface OldOrNewOptions {
|
|||
let options: Options | undefined;
|
||||
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 =
|
||||
(toAllTabs: ToAllTabs | undefined) =>
|
||||
|
@ -132,7 +136,13 @@ export const isAllowed = (localOptions = options) =>
|
|||
!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
|
||||
return {
|
||||
save: save(toAllTabs),
|
||||
|
|
|
@ -16,7 +16,8 @@ getPreloadedState(position, (state) => {
|
|||
preloadedState = state;
|
||||
});
|
||||
|
||||
chrome.runtime.getBackgroundPage(({ store }) => {
|
||||
chrome.runtime.getBackgroundPage((window) => {
|
||||
const { store } = window!;
|
||||
const localStore = configureStore(store, position, preloadedState);
|
||||
let name = 'monitor';
|
||||
if (chrome && chrome.devtools && chrome.devtools.inspectedWindow) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user