mirror of
https://github.com/reduxjs/redux-devtools.git
synced 2024-11-22 01:26:48 +03:00
Split large messages sent from background page to devpanel (#1706)
* Split messages sent to devpanel * Types * Create ninety-files-obey.md
This commit is contained in:
parent
6cf528b4a0
commit
2163bc3f09
5
.changeset/ninety-files-obey.md
Normal file
5
.changeset/ninety-files-obey.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'remotedev-redux-devtools-extension': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Split large messages sent from background page to devpanel
|
|
@ -151,7 +151,7 @@ interface SerializedStateMessage<S, A extends Action<string>> {
|
||||||
readonly committedState: boolean;
|
readonly committedState: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type UpdateStateRequest<S, A extends Action<string>> =
|
export type UpdateStateRequest<S, A extends Action<string>> =
|
||||||
| InitMessage<S, A>
|
| InitMessage<S, A>
|
||||||
| LiftedMessage
|
| LiftedMessage
|
||||||
| SerializedPartialStateMessage
|
| SerializedPartialStateMessage
|
||||||
|
@ -169,6 +169,30 @@ interface UpdateStateAction<S, A extends Action<string>> {
|
||||||
readonly id: string | number;
|
readonly id: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SplitUpdateStateRequestStart<S, A extends Action<string>> = {
|
||||||
|
split: 'start';
|
||||||
|
} & Partial<UpdateStateRequest<S, A>>;
|
||||||
|
|
||||||
|
interface SplitUpdateStateRequestChunk {
|
||||||
|
readonly split: 'chunk';
|
||||||
|
readonly chunk: [string, string];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SplitUpdateStateRequestEnd {
|
||||||
|
readonly split: 'end';
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SplitUpdateStateRequest<S, A extends Action<string>> =
|
||||||
|
| SplitUpdateStateRequestStart<S, A>
|
||||||
|
| SplitUpdateStateRequestChunk
|
||||||
|
| SplitUpdateStateRequestEnd;
|
||||||
|
|
||||||
|
interface SplitUpdateStateAction<S, A extends Action<string>> {
|
||||||
|
readonly type: typeof UPDATE_STATE;
|
||||||
|
request: SplitUpdateStateRequest<S, A>;
|
||||||
|
readonly id: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
export type TabMessage =
|
export type TabMessage =
|
||||||
| StartAction
|
| StartAction
|
||||||
| StopAction
|
| StopAction
|
||||||
|
@ -177,11 +201,16 @@ export type TabMessage =
|
||||||
| ImportAction
|
| ImportAction
|
||||||
| ActionAction
|
| ActionAction
|
||||||
| ExportAction;
|
| ExportAction;
|
||||||
export type PanelMessage<S, A extends Action<string>> =
|
export type PanelMessageWithoutNA<S, A extends Action<string>> =
|
||||||
| NAAction
|
|
||||||
| ErrorMessage
|
| ErrorMessage
|
||||||
| UpdateStateAction<S, A>
|
| UpdateStateAction<S, A>
|
||||||
| SetPersistAction;
|
| SetPersistAction;
|
||||||
|
export type PanelMessage<S, A extends Action<string>> =
|
||||||
|
| PanelMessageWithoutNA<S, A>
|
||||||
|
| NAAction;
|
||||||
|
export type PanelMessageWithSplitAction<S, A extends Action<string>> =
|
||||||
|
| PanelMessage<S, A>
|
||||||
|
| SplitUpdateStateAction<S, A>;
|
||||||
export type MonitorMessage =
|
export type MonitorMessage =
|
||||||
| NAAction
|
| NAAction
|
||||||
| ErrorMessage
|
| ErrorMessage
|
||||||
|
@ -193,7 +222,7 @@ type TabPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
};
|
};
|
||||||
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
type PanelPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
postMessage: <S, A extends Action<string>>(
|
postMessage: <S, A extends Action<string>>(
|
||||||
message: PanelMessage<S, A>,
|
message: PanelMessageWithSplitAction<S, A>,
|
||||||
) => void;
|
) => void;
|
||||||
};
|
};
|
||||||
type MonitorPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
type MonitorPort = Omit<chrome.runtime.Port, 'postMessage'> & {
|
||||||
|
@ -229,21 +258,75 @@ type MonitorAction<S, A extends Action<string>> =
|
||||||
| UpdateStateAction<S, A>
|
| UpdateStateAction<S, A>
|
||||||
| SetPersistAction;
|
| SetPersistAction;
|
||||||
|
|
||||||
|
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||||
|
const maxChromeMsgSize = 32 * 1024 * 1024;
|
||||||
|
|
||||||
function toMonitors<S, A extends Action<string>>(
|
function toMonitors<S, A extends Action<string>>(
|
||||||
action: MonitorAction<S, A>,
|
action: MonitorAction<S, A>,
|
||||||
tabId?: string | number,
|
tabId?: string | number,
|
||||||
verbose?: boolean,
|
verbose?: boolean,
|
||||||
) {
|
) {
|
||||||
Object.keys(connections.monitor).forEach((id) => {
|
for (const monitorPort of Object.values(connections.monitor)) {
|
||||||
connections.monitor[id].postMessage(
|
monitorPort.postMessage(
|
||||||
verbose || action.type === 'ERROR' || action.type === SET_PERSIST
|
verbose || action.type === 'ERROR' || action.type === SET_PERSIST
|
||||||
? action
|
? action
|
||||||
: { type: UPDATE_STATE },
|
: { type: UPDATE_STATE },
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const panelPort of Object.values(connections.panel)) {
|
||||||
|
try {
|
||||||
|
panelPort.postMessage(action);
|
||||||
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
action.type !== UPDATE_STATE ||
|
||||||
|
err == null ||
|
||||||
|
(err as Error).message !==
|
||||||
|
'Message length exceeded maximum allowed length.'
|
||||||
|
) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitMessageStart: SplitUpdateStateRequestStart<S, A> = {
|
||||||
|
split: 'start',
|
||||||
|
};
|
||||||
|
const toSplit: [string, string][] = [];
|
||||||
|
let size = 0;
|
||||||
|
for (const [key, value] of Object.entries(
|
||||||
|
action.request as unknown as Record<string, unknown>,
|
||||||
|
)) {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
size += value.length;
|
||||||
|
if (size > maxChromeMsgSize) {
|
||||||
|
toSplit.push([key, value]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(splitMessageStart as any)[key as keyof typeof splitMessageStart] =
|
||||||
|
value;
|
||||||
|
}
|
||||||
|
|
||||||
|
panelPort.postMessage({ ...action, request: splitMessageStart });
|
||||||
|
|
||||||
|
for (let i = 0; i < toSplit.length; i++) {
|
||||||
|
for (let j = 0; j < toSplit[i][1].length; j += maxChromeMsgSize) {
|
||||||
|
panelPort.postMessage({
|
||||||
|
...action,
|
||||||
|
request: {
|
||||||
|
split: 'chunk',
|
||||||
|
chunk: [
|
||||||
|
toSplit[i][0],
|
||||||
|
toSplit[i][1].substring(j, j + maxChromeMsgSize),
|
||||||
|
],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
Object.keys(connections.panel).forEach((id) => {
|
}
|
||||||
connections.panel[id].postMessage(action);
|
}
|
||||||
});
|
|
||||||
|
panelPort.postMessage({ ...action, request: { split: 'end' } });
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMessage {
|
interface ImportMessage {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
DispatchAction as AppDispatchAction,
|
DispatchAction as AppDispatchAction,
|
||||||
} from '@redux-devtools/app';
|
} from '@redux-devtools/app';
|
||||||
import { LiftedState } from '@redux-devtools/instrument';
|
import { LiftedState } from '@redux-devtools/instrument';
|
||||||
|
|
||||||
const source = '@devtools-extension';
|
const source = '@devtools-extension';
|
||||||
const pageSource = '@devtools-page';
|
const pageSource = '@devtools-page';
|
||||||
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
// Chrome message limit is 64 MB, but we're using 32 MB to include other object's parts
|
||||||
|
|
|
@ -3,12 +3,21 @@ import React, { CSSProperties, ReactNode } from 'react';
|
||||||
import { createRoot, Root } from 'react-dom/client';
|
import { createRoot, Root } from 'react-dom/client';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { Persistor } from 'redux-persist';
|
import { Persistor } from 'redux-persist';
|
||||||
import { REMOVE_INSTANCE, StoreAction } from '@redux-devtools/app';
|
import {
|
||||||
|
REMOVE_INSTANCE,
|
||||||
|
StoreAction,
|
||||||
|
UPDATE_STATE,
|
||||||
|
} from '@redux-devtools/app';
|
||||||
import App from '../app/App';
|
import App from '../app/App';
|
||||||
import configureStore from './store/panelStore';
|
import configureStore from './store/panelStore';
|
||||||
|
|
||||||
import { Action, Store } from 'redux';
|
import { Action, Store } from 'redux';
|
||||||
import type { PanelMessage } from '../background/store/apiMiddleware';
|
import {
|
||||||
|
PanelMessageWithoutNA,
|
||||||
|
PanelMessageWithSplitAction,
|
||||||
|
SplitUpdateStateRequest,
|
||||||
|
UpdateStateRequest,
|
||||||
|
} from '../background/store/apiMiddleware';
|
||||||
import type { StoreStateWithoutSocket } from './store/panelReducer';
|
import type { StoreStateWithoutSocket } from './store/panelReducer';
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
|
|
||||||
|
@ -90,19 +99,59 @@ function renderNA() {
|
||||||
}, 3500);
|
}, 3500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let splitMessage: SplitUpdateStateRequest<unknown, Action<string>>;
|
||||||
|
|
||||||
function init(id: number) {
|
function init(id: number) {
|
||||||
renderNA();
|
renderNA();
|
||||||
bgConnection = chrome.runtime.connect({
|
bgConnection = chrome.runtime.connect({
|
||||||
name: id ? id.toString() : undefined,
|
name: id ? id.toString() : undefined,
|
||||||
});
|
});
|
||||||
bgConnection.onMessage.addListener(
|
bgConnection.onMessage.addListener(
|
||||||
<S, A extends Action<string>>(message: PanelMessage<S, A>) => {
|
<S, A extends Action<string>>(
|
||||||
|
message: PanelMessageWithSplitAction<S, A>,
|
||||||
|
) => {
|
||||||
if (message.type === 'NA') {
|
if (message.type === 'NA') {
|
||||||
if (message.id === id) renderNA();
|
if (message.id === id) renderNA();
|
||||||
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
else store!.dispatch({ type: REMOVE_INSTANCE, id: message.id });
|
||||||
} else {
|
} else {
|
||||||
if (!rendered) renderDevTools();
|
if (!rendered) renderDevTools();
|
||||||
store!.dispatch(message);
|
|
||||||
|
if (
|
||||||
|
message.type === UPDATE_STATE &&
|
||||||
|
(message.request as SplitUpdateStateRequest<S, A>).split
|
||||||
|
) {
|
||||||
|
const request = message.request as SplitUpdateStateRequest<S, A>;
|
||||||
|
|
||||||
|
if (request.split === 'start') {
|
||||||
|
splitMessage = request;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.split === 'chunk') {
|
||||||
|
if ((splitMessage as Record<string, unknown>)[request.chunk[0]]) {
|
||||||
|
(splitMessage as Record<string, unknown>)[request.chunk[0]] +=
|
||||||
|
request.chunk[1];
|
||||||
|
} else {
|
||||||
|
(splitMessage as Record<string, unknown>)[request.chunk[0]] =
|
||||||
|
request.chunk[1];
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (request.split === 'end') {
|
||||||
|
store!.dispatch({
|
||||||
|
...message,
|
||||||
|
request: splitMessage as UpdateStateRequest<S, A>,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Unable to process split message with type: ${(request as any).split}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
store!.dispatch(message as PanelMessageWithoutNA<S, A>);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user