Skip to content

Commit

Permalink
[skip ci] Improve CDP Flow types (#42816)
Browse files Browse the repository at this point in the history
Summary:

Some refactoring to formalise CDP protocol types (`protocol.js` is aligned with [Chrome's `protocol.d.ts` source](https://github.com/ChromeDevTools/devtools-protocol/blob/master/types/protocol.d.ts)), and to demarcate these from other types in the Inspector Proxy prototol.

Changelog: [Internal]

Reviewed By: robhogan

Differential Revision: D53352243
  • Loading branch information
huntie authored and facebook-github-bot committed Feb 6, 2024
1 parent 608029a commit d325e19
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 74 deletions.
59 changes: 33 additions & 26 deletions packages/dev-middleware/src/inspector-proxy/Device.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,11 @@

import type {EventReporter} from '../types/EventReporter';
import type {
DebuggerRequest,
ErrorResponse,
GetScriptSourceRequest,
GetScriptSourceResponse,
MessageFromDevice,
MessageToDevice,
Page,
SetBreakpointByUrlRequest,
} from './types';
CDPClientMessage,
CDPRequest,
CDPResponse,
} from './cdp-types/messages';
import type {MessageFromDevice, MessageToDevice, Page} from './types';

import DeviceEventReporter from './DeviceEventReporter';
import * as fs from 'fs';
Expand Down Expand Up @@ -581,27 +577,32 @@ export default class Device {
}
}

// Allows to make changes in incoming messages from debugger. Returns a boolean
// indicating whether the message has been handled locally (i.e. does not need
// to be forwarded to the target).
/**
* Intercept an incoming message from a connected debugger. Returns either an
* original/replacement CDP message object, or `null` (will forward nothing
* to the target).
*/
#interceptMessageFromDebuggerLegacy(
req: DebuggerRequest,
req: CDPClientMessage,
debuggerInfo: DebuggerInfo,
socket: WS,
): ?DebuggerRequest {
if (req.method === 'Debugger.setBreakpointByUrl') {
return this.#processDebuggerSetBreakpointByUrl(req, debuggerInfo);
} else if (req.method === 'Debugger.getScriptSource') {
this.#processDebuggerGetScriptSource(req, socket);
return null;
): CDPClientMessage | null {
switch (req.method) {
case 'Debugger.setBreakpointByUrl':
return this.#processDebuggerSetBreakpointByUrl(req, debuggerInfo);
case 'Debugger.getScriptSource':
// Sends response to debugger via side-effect
this.#processDebuggerGetScriptSource(req, socket);
return null;
default:
return req;
}
return req;
}

#processDebuggerSetBreakpointByUrl(
req: SetBreakpointByUrlRequest,
req: CDPRequest<'Debugger.setBreakpointByUrl'>,
debuggerInfo: DebuggerInfo,
): SetBreakpointByUrlRequest {
): CDPRequest<'Debugger.setBreakpointByUrl'> {
// If we replaced Android emulator's address to localhost we need to change it back.
if (debuggerInfo.originalSourceURLAddress != null) {
const processedReq = {...req, params: {...req.params}};
Expand Down Expand Up @@ -635,10 +636,16 @@ export default class Device {
return req;
}

#processDebuggerGetScriptSource(req: GetScriptSourceRequest, socket: WS) {
#processDebuggerGetScriptSource(
req: CDPRequest<'Debugger.getScriptSource'>,
socket: WS,
): void {
const sendSuccessResponse = (scriptSource: string) => {
const result: GetScriptSourceResponse = {scriptSource};
const response = {id: req.id, result};
const result = {scriptSource};
const response: CDPResponse<'Debugger.getScriptSource'> = {
id: req.id,
result,
};
socket.send(JSON.stringify(response));
this.#deviceEventReporter?.logResponse(response, 'proxy', {
pageId: this.#debuggerConnection?.pageId ?? null,
Expand All @@ -647,7 +654,7 @@ export default class Device {
};
const sendErrorResponse = (error: string) => {
// Tell the client that the request failed
const result: ErrorResponse = {error: {message: error}};
const result = {error: {message: error}};
const response = {id: req.id, result};
socket.send(JSON.stringify(response));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/

import type {EventReporter} from '../types/EventReporter';
import type {CDPResponse} from './cdp-types/messages';

import TTLCache from '@isaacs/ttlcache';

Expand Down Expand Up @@ -69,11 +70,7 @@ class DeviceEventReporter {
}

logResponse(
res: $ReadOnly<{
id: number,
error?: {message: string, data?: mixed},
...
}>,
res: CDPResponse<>,
origin: 'device' | 'proxy',
metadata: $ReadOnly<{
pageId: string | null,
Expand Down
45 changes: 45 additions & 0 deletions packages/dev-middleware/src/inspector-proxy/cdp-types/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/

import type {Commands, Events} from './protocol';

// Note: A CDP event is a JSON-RPC notification with no `id` member.
export type CDPEvent<TEvent: $Keys<Events> = 'unknown'> = $ReadOnly<{
method: TEvent,
params: Events[TEvent],
}>;

export type CDPRequest<TCommand: $Keys<Commands> = 'unknown'> = $ReadOnly<{
method: TCommand,
params: Commands[TCommand]['paramsType'],
id: number,
}>;

export type CDPResponse<TCommand: $Keys<Commands> = 'unknown'> =
| $ReadOnly<{
result: Commands[TCommand]['resultType'],
id: number,
}>
| $ReadOnly<{
error: CDPRequestError,
id: number,
}>;

export type CDPRequestError = $ReadOnly<{
code: number,
message: string,
data?: mixed,
}>;

export type CDPClientMessage =
| CDPRequest<'Debugger.getScriptSource'>
| CDPRequest<'Debugger.setBreakpointByUrl'>
| CDPRequest<>;
88 changes: 88 additions & 0 deletions packages/dev-middleware/src/inspector-proxy/cdp-types/protocol.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/

// Adapted from https://github.com/ChromeDevTools/devtools-protocol/blob/master/types/protocol.d.ts

export interface Debugger {
GetScriptSourceParams: $ReadOnly<{
/**
* Id of the script to get source for.
*/
scriptId: string,
}>;

GetScriptSourceResult: $ReadOnly<{
/**
* Script source (empty in case of Wasm bytecode).
*/
scriptSource: string,

/**
* Wasm bytecode. (Encoded as a base64 string when passed over JSON)
*/
bytecode?: string,
}>;

SetBreakpointByUrlParams: $ReadOnly<{
/**
* Line number to set breakpoint at.
*/
lineNumber: number,

/**
* URL of the resources to set breakpoint on.
*/
url?: string,

/**
* Regex pattern for the URLs of the resources to set breakpoints on. Either `url` or
* `urlRegex` must be specified.
*/
urlRegex?: string,

/**
* Script hash of the resources to set breakpoint on.
*/
scriptHash?: string,

/**
* Offset in the line to set breakpoint at.
*/
columnNumber?: number,

/**
* Expression to use as a breakpoint condition. When specified, debugger will only stop on the
* breakpoint if this expression evaluates to true.
*/
condition?: string,
}>;
}

export type Events = {
[method: string]: mixed,
};

export type Commands = {
'Debugger.getScriptSource': {
paramsType: Debugger['GetScriptSourceParams'],
resultType: Debugger['GetScriptSourceResult'],
},

'Debugger.setBreakpointByUrl': {
paramsType: Debugger['SetBreakpointByUrlParams'],
resultType: void,
},

[method: string]: {
paramsType: mixed,
resultType: mixed,
},
};
43 changes: 0 additions & 43 deletions packages/dev-middleware/src/inspector-proxy/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,49 +95,6 @@ export type JsonVersionResponse = $ReadOnly<{
'Protocol-Version': string,
}>;

/**
* Types were exported from https://github.com/ChromeDevTools/devtools-protocol/blob/master/types/protocol.d.ts
*/

export type SetBreakpointByUrlRequest = $ReadOnly<{
id: number,
method: 'Debugger.setBreakpointByUrl',
params: $ReadOnly<{
lineNumber: number,
url?: string,
urlRegex?: string,
scriptHash?: string,
columnNumber?: number,
condition?: string,
}>,
}>;

export type GetScriptSourceRequest = $ReadOnly<{
id: number,
method: 'Debugger.getScriptSource',
params: {
scriptId: string,
},
}>;

export type GetScriptSourceResponse = $ReadOnly<{
scriptSource: string,
/**
* Wasm bytecode.
*/
bytecode?: string,
}>;

export type ErrorResponse = $ReadOnly<{
error: $ReadOnly<{
message: string,
}>,
}>;

export type DebuggerRequest =
| SetBreakpointByUrlRequest
| GetScriptSourceRequest;

export type JSONSerializable =
| boolean
| number
Expand Down

0 comments on commit d325e19

Please sign in to comment.