@react-native/dev-middleware 0.74.3 → 0.74.76

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,12 +9,12 @@
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
+ import type { CreateCustomMessageHandlerFn } from "./inspector-proxy/CustomMessageHandler";
12
13
  import type { BrowserLauncher } from "./types/BrowserLauncher";
13
14
  import type { EventReporter } from "./types/EventReporter";
14
15
  import type { ExperimentsConfig } from "./types/Experiments";
15
16
  import type { Logger } from "./types/Logger";
16
17
  import type { NextHandleFunction } from "connect";
17
- import InspectorProxy from "./inspector-proxy/InspectorProxy";
18
18
  type Options = Readonly<{
19
19
  projectRoot: string;
20
20
  /**
@@ -46,11 +46,12 @@ type Options = Readonly<{
46
46
  */
47
47
  unstable_experiments?: ExperimentsConfig;
48
48
  /**
49
- * An interface for using a modified inspector proxy implementation.
49
+ * Create custom handler to add support for unsupported CDP events, or debuggers.
50
+ * This handler is instantiated per logical device and debugger pair.
50
51
  *
51
52
  * This is an unstable API with no semver guarantees.
52
53
  */
53
- unstable_InspectorProxy?: new (...args: any[]) => InspectorProxy;
54
+ unstable_customInspectorMessageHandler?: CreateCustomMessageHandlerFn;
54
55
  }>;
55
56
  type DevMiddlewareAPI = Readonly<{
56
57
  middleware: NextHandleFunction;
@@ -43,15 +43,15 @@ function createDevMiddleware({
43
43
  unstable_browserLauncher = _DefaultBrowserLauncher.default,
44
44
  unstable_eventReporter,
45
45
  unstable_experiments: experimentConfig = {},
46
- unstable_InspectorProxy,
46
+ unstable_customInspectorMessageHandler,
47
47
  }) {
48
48
  const experiments = getExperiments(experimentConfig);
49
- const inspectorProxy = new (unstable_InspectorProxy ??
50
- _InspectorProxy.default)(
49
+ const inspectorProxy = new _InspectorProxy.default(
51
50
  projectRoot,
52
51
  serverBaseUrl,
53
52
  unstable_eventReporter,
54
- experiments
53
+ experiments,
54
+ unstable_customInspectorMessageHandler
55
55
  );
56
56
  const middleware = (0, _connect.default)()
57
57
  .use(
@@ -9,13 +9,13 @@
9
9
  * @oncall react_native
10
10
  */
11
11
 
12
+ import type { CreateCustomMessageHandlerFn } from "./inspector-proxy/CustomMessageHandler";
12
13
  import type { BrowserLauncher } from "./types/BrowserLauncher";
13
14
  import type { EventReporter } from "./types/EventReporter";
14
15
  import type { ExperimentsConfig } from "./types/Experiments";
15
16
  import type { Logger } from "./types/Logger";
16
17
  import type { NextHandleFunction } from "connect";
17
18
 
18
- import InspectorProxy from "./inspector-proxy/InspectorProxy";
19
19
  type Options = $ReadOnly<{
20
20
  projectRoot: string,
21
21
 
@@ -53,11 +53,12 @@ type Options = $ReadOnly<{
53
53
  unstable_experiments?: ExperimentsConfig,
54
54
 
55
55
  /**
56
- * An interface for using a modified inspector proxy implementation.
56
+ * Create custom handler to add support for unsupported CDP events, or debuggers.
57
+ * This handler is instantiated per logical device and debugger pair.
57
58
  *
58
59
  * This is an unstable API with no semver guarantees.
59
60
  */
60
- unstable_InspectorProxy?: Class<InspectorProxy>,
61
+ unstable_customInspectorMessageHandler?: CreateCustomMessageHandlerFn,
61
62
  }>;
62
63
 
63
64
  type DevMiddlewareAPI = $ReadOnly<{
@@ -12,5 +12,8 @@
12
12
  export { default as createDevMiddleware } from "./createDevMiddleware";
13
13
  export type { BrowserLauncher, LaunchedBrowser } from "./types/BrowserLauncher";
14
14
  export type { EventReporter, ReportableEvent } from "./types/EventReporter";
15
- export { default as unstable_InspectorProxy } from "./inspector-proxy/InspectorProxy";
16
- export { default as unstable_Device } from "./inspector-proxy/Device";
15
+ export type {
16
+ CustomMessageHandler,
17
+ CustomMessageHandlerConnection,
18
+ CreateCustomMessageHandlerFn,
19
+ } from "./inspector-proxy/CustomMessageHandler";
@@ -9,25 +9,9 @@ Object.defineProperty(exports, "createDevMiddleware", {
9
9
  return _createDevMiddleware.default;
10
10
  },
11
11
  });
12
- Object.defineProperty(exports, "unstable_Device", {
13
- enumerable: true,
14
- get: function () {
15
- return _Device.default;
16
- },
17
- });
18
- Object.defineProperty(exports, "unstable_InspectorProxy", {
19
- enumerable: true,
20
- get: function () {
21
- return _InspectorProxy.default;
22
- },
23
- });
24
12
  var _createDevMiddleware = _interopRequireDefault(
25
13
  require("./createDevMiddleware")
26
14
  );
27
- var _InspectorProxy = _interopRequireDefault(
28
- require("./inspector-proxy/InspectorProxy")
29
- );
30
- var _Device = _interopRequireDefault(require("./inspector-proxy/Device"));
31
15
  function _interopRequireDefault(obj) {
32
16
  return obj && obj.__esModule ? obj : { default: obj };
33
17
  }
@@ -13,6 +13,8 @@ export { default as createDevMiddleware } from "./createDevMiddleware";
13
13
 
14
14
  export type { BrowserLauncher, LaunchedBrowser } from "./types/BrowserLauncher";
15
15
  export type { EventReporter, ReportableEvent } from "./types/EventReporter";
16
-
17
- export { default as unstable_InspectorProxy } from "./inspector-proxy/InspectorProxy";
18
- export { default as unstable_Device } from "./inspector-proxy/Device";
16
+ export type {
17
+ CustomMessageHandler,
18
+ CustomMessageHandlerConnection,
19
+ CreateCustomMessageHandlerFn,
20
+ } from "./inspector-proxy/CustomMessageHandler";
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ *
8
+ * @format
9
+ */
10
+
11
+ import type { JSONSerializable, Page } from "./types";
12
+ type ExposedDevice = Readonly<{
13
+ appId: string;
14
+ id: string;
15
+ name: string;
16
+ sendMessage: (message: JSONSerializable) => void;
17
+ }>;
18
+ type ExposedDebugger = Readonly<{
19
+ userAgent: string | null;
20
+ sendMessage: (message: JSONSerializable) => void;
21
+ }>;
22
+ export type CustomMessageHandlerConnection = Readonly<{
23
+ page: Page;
24
+ device: ExposedDevice;
25
+ debugger: ExposedDebugger;
26
+ }>;
27
+ export type CreateCustomMessageHandlerFn = (
28
+ connection: CustomMessageHandlerConnection
29
+ ) => null | undefined | CustomMessageHandler;
30
+ /**
31
+ * The device message middleware allows implementers to handle unsupported CDP messages.
32
+ * It is instantiated per device and may contain state that is specific to that device.
33
+ * The middleware can also mark messages from the device or debugger as handled, which stops propagating.
34
+ */
35
+ export interface CustomMessageHandler {
36
+ /**
37
+ * Handle a CDP message coming from the device.
38
+ * This is invoked before the message is sent to the debugger.
39
+ * When returning true, the message is considered handled and will not be sent to the debugger.
40
+ */
41
+ handleDeviceMessage(message: JSONSerializable): true | void;
42
+ /**
43
+ * Handle a CDP message coming from the debugger.
44
+ * This is invoked before the message is sent to the device.
45
+ * When returning true, the message is considered handled and will not be sent to the device.
46
+ */
47
+ handleDebuggerMessage(message: JSONSerializable): true | void;
48
+ }
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ */
10
+
11
+ import type { JSONSerializable, Page } from "./types";
12
+
13
+ type ExposedDevice = $ReadOnly<{
14
+ appId: string,
15
+ id: string,
16
+ name: string,
17
+ sendMessage: (message: JSONSerializable) => void,
18
+ }>;
19
+
20
+ type ExposedDebugger = $ReadOnly<{
21
+ userAgent: string | null,
22
+ sendMessage: (message: JSONSerializable) => void,
23
+ }>;
24
+
25
+ export type CustomMessageHandlerConnection = $ReadOnly<{
26
+ page: Page,
27
+ device: ExposedDevice,
28
+ debugger: ExposedDebugger,
29
+ }>;
30
+
31
+ export type CreateCustomMessageHandlerFn = (
32
+ connection: CustomMessageHandlerConnection
33
+ ) => ?CustomMessageHandler;
34
+
35
+ /**
36
+ * The device message middleware allows implementers to handle unsupported CDP messages.
37
+ * It is instantiated per device and may contain state that is specific to that device.
38
+ * The middleware can also mark messages from the device or debugger as handled, which stops propagating.
39
+ */
40
+ export interface CustomMessageHandler {
41
+ /**
42
+ * Handle a CDP message coming from the device.
43
+ * This is invoked before the message is sent to the debugger.
44
+ * When returning true, the message is considered handled and will not be sent to the debugger.
45
+ */
46
+ handleDeviceMessage(message: JSONSerializable): true | void;
47
+
48
+ /**
49
+ * Handle a CDP message coming from the debugger.
50
+ * This is invoked before the message is sent to the device.
51
+ * When returning true, the message is considered handled and will not be sent to the device.
52
+ */
53
+ handleDebuggerMessage(message: JSONSerializable): true | void;
54
+ }
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import type { EventReporter } from "../types/EventReporter";
13
+ import type { CreateCustomMessageHandlerFn } from "./CustomMessageHandler";
13
14
  import type { Page } from "./types";
14
15
  import WS from "ws";
15
16
  /**
@@ -23,7 +24,8 @@ declare class Device {
23
24
  app: string,
24
25
  socket: WS,
25
26
  projectRoot: string,
26
- eventReporter: null | undefined | EventReporter
27
+ eventReporter: null | undefined | EventReporter,
28
+ createMessageMiddleware: null | undefined | CreateCustomMessageHandlerFn
27
29
  );
28
30
  getName(): string;
29
31
  getApp(): string;
@@ -117,7 +117,18 @@ class Device {
117
117
  #projectRoot;
118
118
  #deviceEventReporter;
119
119
  #pagesPollingIntervalId;
120
- constructor(id, name, app, socket, projectRoot, eventReporter) {
120
+
121
+ // The device message middleware factory function allowing implementers to handle unsupported CDP messages.
122
+ #createCustomMessageHandler;
123
+ constructor(
124
+ id,
125
+ name,
126
+ app,
127
+ socket,
128
+ projectRoot,
129
+ eventReporter,
130
+ createMessageMiddleware
131
+ ) {
121
132
  this.#id = id;
122
133
  this.#name = name;
123
134
  this.#app = app;
@@ -131,6 +142,7 @@ class Device {
131
142
  appId: app,
132
143
  })
133
144
  : null;
145
+ this.#createCustomMessageHandler = createMessageMiddleware;
134
146
 
135
147
  // $FlowFixMe[incompatible-call]
136
148
  this.#deviceSocket.on("message", (message) => {
@@ -175,14 +187,7 @@ class Device {
175
187
  }
176
188
  getPagesList() {
177
189
  if (this.#lastConnectedLegacyReactNativePage) {
178
- const reactNativeReloadablePage = {
179
- id: REACT_NATIVE_RELOADABLE_PAGE_ID,
180
- title: "React Native Experimental (Improved Chrome Reloads)",
181
- vm: "don't use",
182
- app: this.#app,
183
- capabilities: {},
184
- };
185
- return [...this.#pages.values(), reactNativeReloadablePage];
190
+ return [...this.#pages.values(), this.#createSyntheticPage()];
186
191
  } else {
187
192
  return [...this.#pages.values()];
188
193
  }
@@ -210,13 +215,59 @@ class Device {
210
215
  prependedFilePrefix: false,
211
216
  pageId,
212
217
  userAgent: metadata.userAgent,
218
+ customHandler: null,
213
219
  };
214
220
 
215
221
  // TODO(moti): Handle null case explicitly, e.g. refuse to connect to
216
222
  // unknown pages.
217
- const page = this.#pages.get(pageId);
223
+ const page =
224
+ pageId === REACT_NATIVE_RELOADABLE_PAGE_ID
225
+ ? this.#createSyntheticPage()
226
+ : this.#pages.get(pageId);
218
227
  this.#debuggerConnection = debuggerInfo;
219
228
  debug(`Got new debugger connection for page ${pageId} of ${this.#name}`);
229
+ if (page && this.#debuggerConnection && this.#createCustomMessageHandler) {
230
+ this.#debuggerConnection.customHandler = this.#createCustomMessageHandler(
231
+ {
232
+ page,
233
+ debugger: {
234
+ userAgent: debuggerInfo.userAgent,
235
+ sendMessage: (message) => {
236
+ try {
237
+ const payload = JSON.stringify(message);
238
+ debug("(Debugger) <- (Proxy) (Device): " + payload);
239
+ socket.send(payload);
240
+ } catch {}
241
+ },
242
+ },
243
+ device: {
244
+ appId: this.#app,
245
+ id: this.#id,
246
+ name: this.#name,
247
+ sendMessage: (message) => {
248
+ try {
249
+ const payload = JSON.stringify({
250
+ event: "wrappedEvent",
251
+ payload: {
252
+ pageId: this.#mapToDevicePageId(pageId),
253
+ wrappedEvent: JSON.stringify(message),
254
+ },
255
+ });
256
+ debug("(Debugger) -> (Proxy) (Device): " + payload);
257
+ this.#deviceSocket.send(payload);
258
+ } catch {}
259
+ },
260
+ },
261
+ }
262
+ );
263
+ if (this.#debuggerConnection.customHandler) {
264
+ debug("Created new custom message handler for debugger connection");
265
+ } else {
266
+ debug(
267
+ "Skipping new custom message handler for debugger connection, factory function returned null"
268
+ );
269
+ }
270
+ }
220
271
  this.#sendMessageToDevice({
221
272
  event: "connect",
222
273
  payload: {
@@ -233,6 +284,13 @@ class Device {
233
284
  frontendUserAgent: metadata.userAgent,
234
285
  });
235
286
  let processedReq = debuggerRequest;
287
+ if (
288
+ this.#debuggerConnection?.customHandler?.handleDebuggerMessage(
289
+ debuggerRequest
290
+ ) === true
291
+ ) {
292
+ return;
293
+ }
236
294
  if (!page || !this.#pageHasCapability(page, "nativeSourceCodeFetching")) {
237
295
  processedReq = this.#interceptClientMessageForSourceFetching(
238
296
  debuggerRequest,
@@ -310,6 +368,19 @@ class Device {
310
368
  return page.capabilities[flag] === true;
311
369
  }
312
370
 
371
+ /**
372
+ * Returns the synthetic "React Native Experimental (Improved Chrome Reloads)" page.
373
+ */
374
+ #createSyntheticPage() {
375
+ return {
376
+ id: REACT_NATIVE_RELOADABLE_PAGE_ID,
377
+ title: "React Native Experimental (Improved Chrome Reloads)",
378
+ vm: "don't use",
379
+ app: this.#app,
380
+ capabilities: {},
381
+ };
382
+ }
383
+
313
384
  // Handles messages received from device:
314
385
  // 1. For getPages responses updates local #pages list.
315
386
  // 2. All other messages are forwarded to debugger as wrappedEvent.
@@ -412,12 +483,21 @@ class Device {
412
483
  frontendUserAgent: this.#debuggerConnection?.userAgent ?? null,
413
484
  });
414
485
  }
415
- if (this.#debuggerConnection != null) {
486
+ const debuggerConnection = this.#debuggerConnection;
487
+ if (debuggerConnection != null) {
488
+ if (
489
+ debuggerConnection.customHandler?.handleDeviceMessage(
490
+ parsedPayload
491
+ ) === true
492
+ ) {
493
+ return;
494
+ }
495
+
416
496
  // Wrapping just to make flow happy :)
417
497
  // $FlowFixMe[unused-promise]
418
498
  this.#processMessageFromDeviceLegacy(
419
499
  parsedPayload,
420
- this.#debuggerConnection,
500
+ debuggerConnection,
421
501
  pageId
422
502
  ).then(() => {
423
503
  const messageToSend = JSON.stringify(parsedPayload);
@@ -755,10 +835,6 @@ class Device {
755
835
  // Fetch text, raising an exception if the text could not be fetched,
756
836
  // or is too large.
757
837
  async #fetchText(url) {
758
- if (!["localhost", "127.0.0.1"].includes(url.hostname)) {
759
- throw new Error("remote fetches not permitted");
760
- }
761
-
762
838
  // $FlowFixMe[incompatible-call] Suppress arvr node-fetch flow error
763
839
  const response = await (0, _nodeFetch.default)(url);
764
840
  if (!response.ok) {
@@ -10,6 +10,7 @@
10
10
  */
11
11
 
12
12
  import type { EventReporter } from "../types/EventReporter";
13
+ import type { CreateCustomMessageHandlerFn } from "./CustomMessageHandler";
13
14
  import type { Page } from "./types";
14
15
 
15
16
  import WS from "ws";
@@ -25,7 +26,8 @@ declare export default class Device {
25
26
  app: string,
26
27
  socket: WS,
27
28
  projectRoot: string,
28
- eventReporter: ?EventReporter
29
+ eventReporter: ?EventReporter,
30
+ createMessageMiddleware: ?CreateCustomMessageHandlerFn
29
31
  ): void;
30
32
  getName(): string;
31
33
  getApp(): string;
@@ -11,6 +11,7 @@
11
11
 
12
12
  import type { EventReporter } from "../types/EventReporter";
13
13
  import type { Experiments } from "../types/Experiments";
14
+ import type { CreateCustomMessageHandlerFn } from "./CustomMessageHandler";
14
15
  import type { PageDescription } from "./types";
15
16
  import type { IncomingMessage, ServerResponse } from "http";
16
17
  import WS from "ws";
@@ -25,7 +26,8 @@ declare class InspectorProxy implements InspectorProxyQueries {
25
26
  projectRoot: string,
26
27
  serverBaseUrl: string,
27
28
  eventReporter: null | undefined | EventReporter,
28
- experiments: Experiments
29
+ experiments: Experiments,
30
+ customMessageHandler: null | undefined | CreateCustomMessageHandlerFn
29
31
  );
30
32
  getPageDescriptions(): Array<PageDescription>;
31
33
  processRequest(
@@ -46,12 +46,22 @@ class InspectorProxy {
46
46
  #deviceCounter = 0;
47
47
  #eventReporter;
48
48
  #experiments;
49
- constructor(projectRoot, serverBaseUrl, eventReporter, experiments) {
49
+
50
+ // custom message handler factory allowing implementers to handle unsupported CDP messages.
51
+ #customMessageHandler;
52
+ constructor(
53
+ projectRoot,
54
+ serverBaseUrl,
55
+ eventReporter,
56
+ experiments,
57
+ customMessageHandler
58
+ ) {
50
59
  this.#projectRoot = projectRoot;
51
60
  this.#serverBaseUrl = serverBaseUrl;
52
61
  this.#devices = new Map();
53
62
  this.#eventReporter = eventReporter;
54
63
  this.#experiments = experiments;
64
+ this.#customMessageHandler = customMessageHandler;
55
65
  }
56
66
  getPageDescriptions() {
57
67
  // Build list of pages from all devices.
@@ -163,7 +173,8 @@ class InspectorProxy {
163
173
  appName,
164
174
  socket,
165
175
  this.#projectRoot,
166
- this.#eventReporter
176
+ this.#eventReporter,
177
+ this.#customMessageHandler
167
178
  );
168
179
  if (oldDevice) {
169
180
  oldDevice.handleDuplicateDeviceConnection(newDevice);
@@ -211,7 +222,7 @@ class InspectorProxy {
211
222
  throw new Error("Unknown device with ID " + deviceId);
212
223
  }
213
224
  device.handleDebuggerConnection(socket, pageId, {
214
- userAgent: req.headers["user-agent"] ?? null,
225
+ userAgent: req.headers["user-agent"] ?? query.userAgent ?? null,
215
226
  });
216
227
  } catch (e) {
217
228
  console.error(e);
@@ -11,6 +11,7 @@
11
11
 
12
12
  import type { EventReporter } from "../types/EventReporter";
13
13
  import type { Experiments } from "../types/Experiments";
14
+ import type { CreateCustomMessageHandlerFn } from "./CustomMessageHandler";
14
15
  import type { PageDescription } from "./types";
15
16
  import type { IncomingMessage, ServerResponse } from "http";
16
17
 
@@ -28,7 +29,8 @@ declare export default class InspectorProxy implements InspectorProxyQueries {
28
29
  projectRoot: string,
29
30
  serverBaseUrl: string,
30
31
  eventReporter: ?EventReporter,
31
- experiments: Experiments
32
+ experiments: Experiments,
33
+ customMessageHandler: ?CreateCustomMessageHandlerFn
32
34
  ): void;
33
35
  getPageDescriptions(): Array<PageDescription>;
34
36
  processRequest(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-native/dev-middleware",
3
- "version": "0.74.3",
3
+ "version": "0.74.76",
4
4
  "description": "Dev server middleware for React Native",
5
5
  "keywords": ["react-native", "tools"],
6
6
  "homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/dev-middleware#readme",
@@ -15,7 +15,7 @@
15
15
  "files": ["dist"],
16
16
  "dependencies": {
17
17
  "@isaacs/ttlcache": "^1.4.1",
18
- "@react-native/debugger-frontend": "0.74.1",
18
+ "@react-native/debugger-frontend": "0.74.76",
19
19
  "@rnx-kit/chromium-edge-launcher": "^1.0.0",
20
20
  "chrome-launcher": "^0.15.2",
21
21
  "connect": "^3.6.5",