mirror of
				https://github.com/reduxjs/redux-devtools.git
				synced 2025-10-26 05:31:16 +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 }, | ||||||
|     ); |     ); | ||||||
|   }); |   } | ||||||
|   Object.keys(connections.panel).forEach((id) => { | 
 | ||||||
|     connections.panel[id].postMessage(action); |   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), | ||||||
|  |               ], | ||||||
|  |             }, | ||||||
|  |           }); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       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