@rozenite/network-activity-plugin 1.0.0-alpha.9 → 1.1.0

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.
Files changed (113) hide show
  1. package/README.md +2 -0
  2. package/dist/App.html +2 -2
  3. package/dist/assets/{App-DoHQsY5s.css → App-BrSkOkws.css} +223 -2
  4. package/dist/assets/{App-CA1Fbh0I.js → App-Kyi7zHUX.js} +8188 -2671
  5. package/dist/react-native.cjs +4 -1
  6. package/dist/react-native.js +4 -1
  7. package/dist/rozenite.json +1 -1
  8. package/dist/src/react-native/config.d.ts +20 -0
  9. package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
  10. package/dist/src/react-native/http/xhr-interceptor.d.ts +7 -1
  11. package/dist/src/react-native/sse/sse-interceptor.d.ts +2 -2
  12. package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -1
  13. package/dist/src/react-native/utils/getBlobName.d.ts +35 -0
  14. package/dist/src/react-native/utils/getFormDataEntries.d.ts +18 -0
  15. package/dist/src/shared/client.d.ts +55 -4
  16. package/dist/src/shared/sse-events.d.ts +4 -1
  17. package/dist/src/ui/components/Button.d.ts +2 -2
  18. package/dist/src/ui/components/CodeBlock.d.ts +3 -0
  19. package/dist/src/ui/components/CodeEditor.d.ts +5 -0
  20. package/dist/src/ui/components/CookieCard.d.ts +7 -0
  21. package/dist/src/ui/components/CopyRequestDropdown.d.ts +7 -0
  22. package/dist/src/ui/components/DropdownMenu.d.ts +27 -0
  23. package/dist/src/ui/components/FilterBar.d.ts +10 -0
  24. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +1 -1
  25. package/dist/src/ui/components/KeyValueGrid.d.ts +13 -0
  26. package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
  27. package/dist/src/ui/components/RequestBody.d.ts +6 -0
  28. package/dist/src/ui/components/RequestList.d.ts +9 -4
  29. package/dist/src/ui/components/ScrollArea.d.ts +3 -2
  30. package/dist/src/ui/components/Section.d.ts +8 -0
  31. package/dist/src/ui/components/Separator.d.ts +2 -1
  32. package/dist/src/ui/components/Tabs.d.ts +7 -0
  33. package/dist/src/ui/state/hooks.d.ts +4 -0
  34. package/dist/src/ui/state/model.d.ts +22 -7
  35. package/dist/src/ui/state/store.d.ts +27 -3
  36. package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +2 -0
  37. package/dist/src/ui/utils/escapeShellArg.d.ts +1 -0
  38. package/dist/src/ui/utils/generateCurlCommand.d.ts +2 -0
  39. package/dist/src/ui/utils/generateFetchCall.d.ts +2 -0
  40. package/dist/src/ui/utils/generateMultipartBody.d.ts +4 -0
  41. package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +7 -0
  42. package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +9 -0
  43. package/dist/src/utils/cookieParser.d.ts +6 -0
  44. package/dist/src/utils/getContentTypeMimeType.d.ts +2 -0
  45. package/dist/src/utils/getHttpHeader.d.ts +5 -0
  46. package/dist/src/utils/getHttpHeaderValueAsString.d.ts +11 -0
  47. package/dist/src/utils/getStringSizeInBytes.d.ts +1 -0
  48. package/dist/src/utils/inferContentTypeFromPostData.d.ts +2 -0
  49. package/dist/src/utils/safeStringify.d.ts +1 -0
  50. package/dist/src/utils/typeChecks.d.ts +9 -0
  51. package/dist/useNetworkActivityDevTools.cjs +337 -24
  52. package/dist/useNetworkActivityDevTools.js +338 -25
  53. package/package.json +7 -4
  54. package/react-native.ts +6 -1
  55. package/src/react-native/config.ts +43 -0
  56. package/src/react-native/http/network-inspector.ts +190 -8
  57. package/src/react-native/http/overrides-registry.ts +32 -0
  58. package/src/react-native/http/xhr-interceptor.ts +19 -2
  59. package/src/react-native/sse/sse-inspector.ts +27 -5
  60. package/src/react-native/sse/sse-interceptor.ts +26 -8
  61. package/src/react-native/useNetworkActivityDevTools.ts +86 -8
  62. package/src/react-native/utils/getBlobName.ts +45 -0
  63. package/src/react-native/utils/getFormDataEntries.ts +32 -0
  64. package/src/react-native/utils.ts +3 -3
  65. package/src/shared/client.ts +81 -4
  66. package/src/shared/sse-events.ts +4 -1
  67. package/src/ui/components/Button.tsx +1 -0
  68. package/src/ui/components/CodeBlock.tsx +19 -0
  69. package/src/ui/components/CodeEditor.tsx +26 -0
  70. package/src/ui/components/CookieCard.tsx +64 -0
  71. package/src/ui/components/CopyRequestDropdown.tsx +95 -0
  72. package/src/ui/components/DropdownMenu.tsx +206 -0
  73. package/src/ui/components/FilterBar.tsx +117 -0
  74. package/src/ui/components/Input.tsx +1 -1
  75. package/src/ui/components/JsonTree.tsx +10 -3
  76. package/src/ui/components/JsonTreeCopyableItem.tsx +14 -10
  77. package/src/ui/components/KeyValueGrid.tsx +51 -0
  78. package/src/ui/components/OverrideResponse.tsx +132 -0
  79. package/src/ui/components/RequestBody.tsx +86 -0
  80. package/src/ui/components/RequestList.tsx +74 -14
  81. package/src/ui/components/ScrollArea.tsx +1 -0
  82. package/src/ui/components/Section.tsx +46 -0
  83. package/src/ui/components/SidePanel.tsx +15 -5
  84. package/src/ui/components/Toolbar.tsx +3 -2
  85. package/src/ui/globals.css +4 -0
  86. package/src/ui/hooks/useCopyToClipboard.ts +2 -2
  87. package/src/ui/state/derived.ts +2 -0
  88. package/src/ui/state/hooks.ts +8 -0
  89. package/src/ui/state/model.ts +28 -7
  90. package/src/ui/state/store.ts +640 -500
  91. package/src/ui/tabs/CookiesTab.tsx +60 -263
  92. package/src/ui/tabs/HeadersTab.tsx +78 -89
  93. package/src/ui/tabs/RequestTab.tsx +58 -46
  94. package/src/ui/tabs/ResponseTab.tsx +98 -67
  95. package/src/ui/tabs/SSEMessagesTab.tsx +50 -39
  96. package/src/ui/utils/checkRequestBodyBinary.ts +7 -0
  97. package/src/ui/utils/escapeShellArg.ts +12 -0
  98. package/src/ui/utils/generateCurlCommand.ts +83 -0
  99. package/src/ui/utils/generateFetchCall.ts +64 -0
  100. package/src/ui/utils/generateMultipartBody.ts +19 -0
  101. package/src/ui/views/InspectorView.tsx +15 -3
  102. package/src/utils/applyReactNativeRequestHeadersLogic.ts +30 -0
  103. package/src/utils/applyReactNativeResponseHeadersLogic.ts +28 -0
  104. package/src/utils/cookieParser.ts +126 -0
  105. package/src/utils/getContentTypeMimeType.ts +17 -0
  106. package/src/utils/getHttpHeader.ts +17 -0
  107. package/src/utils/getHttpHeaderValueAsString.ts +13 -0
  108. package/src/utils/getStringSizeInBytes.ts +3 -0
  109. package/src/utils/inferContentTypeFromPostData.ts +9 -0
  110. package/src/utils/safeStringify.ts +7 -0
  111. package/src/utils/typeChecks.ts +27 -0
  112. package/dist/src/ui/utils/getHttpHeaderValue.d.ts +0 -2
  113. package/src/ui/utils/getHttpHeaderValue.ts +0 -14
@@ -0,0 +1,9 @@
1
+ import { HttpHeaders, XHRHeaders } from '../shared/client';
2
+ /**
3
+ * Applies React Native specific logic to response headers.
4
+ * React Native concatenates multiple header values into single strings,
5
+ * this function parses them back into arrays where appropriate.
6
+ *
7
+ * @see https://github.com/facebook/react-native/blob/588f0c5ce6c283f116228456da2170d2adc3cbf4/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java#L637
8
+ */
9
+ export declare const applyReactNativeResponseHeadersLogic: (headers: XHRHeaders) => HttpHeaders;
@@ -0,0 +1,6 @@
1
+ import { Cookie, HttpHeaders } from '../shared/client';
2
+ export declare const parseSetCookieHeader: (setCookieStr: string) => Cookie;
3
+ export declare const splitSetCookieHeaderByComma: (header: string) => string[];
4
+ export declare const parseCookieHeader: (cookieString: string) => Cookie[];
5
+ export declare const parseRequestCookiesFromHeaders: (headers: HttpHeaders) => Cookie[];
6
+ export declare const parseResponseCookiesFromHeaders: (headers: HttpHeaders) => Cookie[];
@@ -0,0 +1,2 @@
1
+ import { HttpHeaders } from '../shared/client';
2
+ export declare function getContentTypeMime(headers: HttpHeaders): string | undefined;
@@ -0,0 +1,5 @@
1
+ import { HttpHeaders, XHRHeaders } from '../shared/client';
2
+ export declare function getHttpHeader<T extends HttpHeaders | XHRHeaders>(headers: T, name: string): {
3
+ value: T[Extract<keyof T, string>];
4
+ originalKey: Extract<keyof T, string>;
5
+ } | undefined;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Combines multiple HTTP header values according to RFC 7230 Section 3.2.2
3
+ *
4
+ * Per RFC 7230 Section 3.2.2: "A recipient MAY combine multiple header fields
5
+ * with the same field name into one 'field-name: field-value' pair, without
6
+ * changing the semantics of the message, by appending each subsequent field
7
+ * value to the combined field value in order, separated by a comma."
8
+ *
9
+ * @see https://tools.ietf.org/html/rfc7230#section-3.2.2
10
+ */
11
+ export declare function getHttpHeaderValueAsString(value: string | string[]): string;
@@ -0,0 +1 @@
1
+ export declare const getStringSizeInBytes: (value: string) => number;
@@ -0,0 +1,2 @@
1
+ import { RequestPostData } from '../shared/client';
2
+ export declare function inferContentTypeFromPostData(postData: RequestPostData): "multipart/form-data" | undefined;
@@ -0,0 +1 @@
1
+ export declare function safeStringify(data: unknown): string;
@@ -0,0 +1,9 @@
1
+ export declare const isBlob: (value: unknown) => value is Blob;
2
+ export declare const isArrayBuffer: (value: unknown) => value is ArrayBuffer | ArrayBufferView;
3
+ export declare const isFormData: (value: unknown) => value is FormData;
4
+ export declare const isNullOrUndefined: (value: unknown) => value is null | undefined;
5
+ export declare const isString: (value: unknown) => value is string;
6
+ export declare const isNumber: (value: unknown) => value is number;
7
+ export declare const isBoolean: (value: unknown) => value is boolean;
8
+ export declare const isObject: (value: unknown) => value is object;
9
+ export declare const isArray: (value: unknown) => value is unknown[];
@@ -8,21 +8,37 @@ const WebSocketInterceptor = require("react-native/Libraries/WebSocket/WebSocket
8
8
  const eventSource = require("./event-source.cjs");
9
9
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
10
  const WebSocketInterceptor__default = /* @__PURE__ */ _interopDefault(WebSocketInterceptor);
11
- function getHttpHeaderValue(headers, name) {
11
+ function safeStringify(data) {
12
+ try {
13
+ return typeof data === "string" ? data : JSON.stringify(data);
14
+ } catch {
15
+ return String(data);
16
+ }
17
+ }
18
+ function getHttpHeader(headers, name) {
12
19
  const lowerName = name.toLowerCase();
13
20
  for (const key in headers) {
14
21
  if (key.toLowerCase() === lowerName) {
15
- return headers[key];
22
+ return { value: headers[key], originalKey: key };
16
23
  }
17
24
  }
18
25
  return void 0;
19
26
  }
27
+ function getContentTypeMime(headers) {
28
+ const contentType = getHttpHeader(headers, "content-type");
29
+ if (!contentType) {
30
+ return void 0;
31
+ }
32
+ const { value } = contentType;
33
+ const actualValue = Array.isArray(value) ? value[0] : value;
34
+ return actualValue.split(";")[0].trim();
35
+ }
20
36
  const getContentType = (request) => {
21
37
  const responseHeaders = request.responseHeaders;
22
38
  const responseType = request.responseType;
23
- const contentType = getHttpHeaderValue(responseHeaders || {}, "content-type");
39
+ const contentType = getContentTypeMime(responseHeaders || {});
24
40
  if (contentType) {
25
- return contentType.split(";")[0].trim();
41
+ return contentType;
26
42
  }
27
43
  switch (responseType) {
28
44
  case "arraybuffer":
@@ -70,6 +86,28 @@ const getNetworkRequestsRegistry = () => {
70
86
  clear
71
87
  };
72
88
  };
89
+ function getBlobName(blob) {
90
+ if (typeof (blob == null ? void 0 : blob.name) === "string") {
91
+ return blob.name;
92
+ }
93
+ if ((blob == null ? void 0 : blob.data) && typeof blob.data.name === "string") {
94
+ return blob.data.name;
95
+ }
96
+ return void 0;
97
+ }
98
+ function getFormDataEntries(formData) {
99
+ if (!formData || typeof formData !== "object") {
100
+ return [];
101
+ }
102
+ if (typeof formData.entries === "function") {
103
+ return formData.entries();
104
+ }
105
+ if (Array.isArray(formData._parts)) {
106
+ return formData._parts;
107
+ }
108
+ return [];
109
+ }
110
+ const XMLHttpRequest = global.XMLHttpRequest || window.XMLHttpRequest;
73
111
  const originalXHROpen = XMLHttpRequest.prototype.open;
74
112
  const originalXHRSend = XMLHttpRequest.prototype.send;
75
113
  const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
@@ -78,6 +116,7 @@ let sendCallback;
78
116
  let requestHeaderCallback;
79
117
  let headerReceivedCallback;
80
118
  let responseCallback;
119
+ let overrideCallback;
81
120
  let isInterceptorEnabled$1 = false;
82
121
  const XHRInterceptor = {
83
122
  /**
@@ -110,6 +149,12 @@ const XHRInterceptor = {
110
149
  setRequestHeaderCallback(callback) {
111
150
  requestHeaderCallback = callback;
112
151
  },
152
+ /**
153
+ * Invoked before XMLHttpRequest.send(...) is called.
154
+ */
155
+ setOverrideCallback(callback) {
156
+ overrideCallback = callback;
157
+ },
113
158
  isInterceptorEnabled() {
114
159
  return isInterceptorEnabled$1;
115
160
  },
@@ -133,6 +178,9 @@ const XHRInterceptor = {
133
178
  if (sendCallback) {
134
179
  sendCallback(data, this);
135
180
  }
181
+ if (overrideCallback) {
182
+ overrideCallback(this);
183
+ }
136
184
  if (this.addEventListener) {
137
185
  this.addEventListener(
138
186
  "readystatechange",
@@ -193,14 +241,128 @@ const XHRInterceptor = {
193
241
  sendCallback = null;
194
242
  headerReceivedCallback = null;
195
243
  requestHeaderCallback = null;
244
+ overrideCallback = null;
245
+ }
246
+ };
247
+ const getStringSizeInBytes = (value) => {
248
+ return new TextEncoder().encode(value).length;
249
+ };
250
+ const splitSetCookieHeaderByComma = (header) => {
251
+ const regex = /(?:^|,\s)([^=;,]+=[^;]+(?:;[^,]*)*)/g;
252
+ const matches = [];
253
+ let match;
254
+ while ((match = regex.exec(header)) !== null) {
255
+ matches.push(match[1].trim());
256
+ }
257
+ return matches;
258
+ };
259
+ const applyReactNativeResponseHeadersLogic = (headers) => {
260
+ const parsedHeaders = { ...headers };
261
+ const setCookieHeader = getHttpHeader(headers, "set-cookie");
262
+ if (setCookieHeader) {
263
+ const { value, originalKey } = setCookieHeader;
264
+ const cookies = splitSetCookieHeaderByComma(value);
265
+ parsedHeaders[originalKey] = cookies.length > 0 ? cookies : value;
266
+ }
267
+ return parsedHeaders;
268
+ };
269
+ const isBlob = (value) => value instanceof Blob;
270
+ const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
271
+ const isFormData = (value) => value instanceof FormData;
272
+ const isNullOrUndefined = (value) => value === null || value === void 0;
273
+ const createOverridesRegistry = () => {
274
+ let overrides = /* @__PURE__ */ new Map();
275
+ const setOverrides = (newOverrides) => {
276
+ overrides = new Map(newOverrides);
277
+ };
278
+ const getOverrideForUrl = (url) => {
279
+ return overrides.get(url);
280
+ };
281
+ return {
282
+ setOverrides,
283
+ getOverrideForUrl
284
+ };
285
+ };
286
+ let registryInstance = null;
287
+ const getOverridesRegistry = () => {
288
+ if (!registryInstance) {
289
+ registryInstance = createOverridesRegistry();
196
290
  }
291
+ return registryInstance;
197
292
  };
198
293
  const networkRequestsRegistry = getNetworkRequestsRegistry();
294
+ const overridesRegistry$1 = getOverridesRegistry();
295
+ const getBinaryPostData = (body) => ({
296
+ type: "binary",
297
+ value: {
298
+ size: body.size,
299
+ type: body.type,
300
+ name: getBlobName(body)
301
+ }
302
+ });
303
+ const getArrayBufferPostData = (body) => ({
304
+ type: "binary",
305
+ value: {
306
+ size: body.byteLength
307
+ }
308
+ });
309
+ const getTextPostData = (body) => ({
310
+ type: "text",
311
+ value: safeStringify(body)
312
+ });
313
+ const getFormDataPostData = (body) => ({
314
+ type: "form-data",
315
+ value: getFormDataEntries(body).reduce(
316
+ (acc, [key, value]) => {
317
+ if (isBlob(value)) {
318
+ acc[key] = getBinaryPostData(value);
319
+ } else if (isArrayBuffer(value)) {
320
+ acc[key] = getArrayBufferPostData(value);
321
+ } else {
322
+ acc[key] = getTextPostData(value);
323
+ }
324
+ return acc;
325
+ },
326
+ {}
327
+ )
328
+ });
329
+ const getRequestBody = (body) => {
330
+ if (isNullOrUndefined(body)) {
331
+ return body;
332
+ }
333
+ if (isBlob(body)) {
334
+ return getBinaryPostData(body);
335
+ }
336
+ if (isArrayBuffer(body)) {
337
+ return getArrayBufferPostData(body);
338
+ }
339
+ if (isFormData(body)) {
340
+ return getFormDataPostData(body);
341
+ }
342
+ return getTextPostData(body);
343
+ };
199
344
  const getResponseSize = (request) => {
200
- if (typeof request.response === "object") {
201
- return request.response.size;
345
+ try {
346
+ const { responseType, response } = request;
347
+ if (response === null) {
348
+ return 0;
349
+ }
350
+ if (responseType === "" || responseType === "text") {
351
+ return getStringSizeInBytes(request.responseText);
352
+ }
353
+ if (responseType === "json") {
354
+ return getStringSizeInBytes(safeStringify(response));
355
+ }
356
+ if (responseType === "blob") {
357
+ return response.size;
358
+ }
359
+ if (responseType === "arraybuffer") {
360
+ return response.byteLength;
361
+ }
362
+ return 0;
363
+ } catch {
364
+ return null;
202
365
  }
203
- return request.response.length || 0;
204
366
  };
205
367
  const getResponseBody = async (request) => {
206
368
  const responseType = request.responseType;
@@ -219,6 +381,9 @@ const getResponseBody = async (request) => {
219
381
  });
220
382
  }
221
383
  }
384
+ if (responseType === "json") {
385
+ return safeStringify(request.response);
386
+ }
222
387
  return null;
223
388
  };
224
389
  const getInitiatorFromStack = () => {
@@ -260,11 +425,20 @@ const getNetworkInspector = (pluginClient) => {
260
425
  url: request._url,
261
426
  method: request._method,
262
427
  headers: request._headers,
263
- postData: data
428
+ postData: getRequestBody(data)
264
429
  },
265
430
  type: "XHR",
266
431
  initiator
267
432
  });
433
+ request.addEventListener("progress", (event) => {
434
+ pluginClient.send("request-progress", {
435
+ requestId,
436
+ timestamp: Date.now(),
437
+ loaded: event.loaded,
438
+ total: event.total,
439
+ lengthComputable: event.lengthComputable
440
+ });
441
+ });
268
442
  request.addEventListener("readystatechange", () => {
269
443
  if (request.readyState === READY_STATE_HEADERS_RECEIVED) {
270
444
  ttfb = Date.now() - sendTime;
@@ -279,7 +453,9 @@ const getNetworkInspector = (pluginClient) => {
279
453
  url: request._url,
280
454
  status: request.status,
281
455
  statusText: request.statusText,
282
- headers: request.responseHeaders || {},
456
+ headers: applyReactNativeResponseHeadersLogic(
457
+ request.responseHeaders || {}
458
+ ),
283
459
  contentType: getContentType(request),
284
460
  size: getResponseSize(request),
285
461
  responseTime: Date.now()
@@ -313,10 +489,55 @@ const getNetworkInspector = (pluginClient) => {
313
489
  canceled: true
314
490
  });
315
491
  });
492
+ request.addEventListener("timeout", () => {
493
+ pluginClient.send("request-failed", {
494
+ requestId,
495
+ timestamp: Date.now(),
496
+ type: "XHR",
497
+ error: "Timeout",
498
+ canceled: false
499
+ });
500
+ });
501
+ };
502
+ const handleRequestOverride = (request) => {
503
+ const override = overridesRegistry$1.getOverrideForUrl(
504
+ request._url
505
+ );
506
+ if (!override) {
507
+ return;
508
+ }
509
+ request.addEventListener("readystatechange", () => {
510
+ if (override.body !== void 0) {
511
+ Object.defineProperty(request, "responseType", {
512
+ writable: true
513
+ });
514
+ Object.defineProperty(request, "response", {
515
+ writable: true
516
+ });
517
+ Object.defineProperty(request, "responseText", {
518
+ writable: true
519
+ });
520
+ const contentType = getContentType(request);
521
+ if (contentType === "application/json") {
522
+ request.responseType = "json";
523
+ } else if (contentType === "text/plain") {
524
+ request.responseType = "text";
525
+ }
526
+ request.response = override.body;
527
+ request.responseText = override.body;
528
+ }
529
+ if (override.status !== void 0) {
530
+ Object.defineProperty(request, "status", {
531
+ writable: true
532
+ });
533
+ request.status = override.status;
534
+ }
535
+ });
316
536
  };
317
537
  const enable = () => {
318
538
  XHRInterceptor.disableInterception();
319
539
  XHRInterceptor.setSendCallback(handleRequestSend);
540
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
320
541
  XHRInterceptor.enableInterception();
321
542
  };
322
543
  const disable = () => {
@@ -522,6 +743,8 @@ let closeCallback;
522
743
  let isInterceptorEnabled = false;
523
744
  const eventSourceClass = eventSource.getEventSource();
524
745
  const originalOpen = eventSourceClass.prototype.open;
746
+ const originalDispatch = eventSourceClass.prototype.dispatch;
747
+ const BUILT_IN_EVENT_TYPES = /* @__PURE__ */ new Set(["open", "error", "close", "done"]);
525
748
  const SSEInterceptor = {
526
749
  /**
527
750
  * Invoked when EventSource.open() is called (connection attempt starting).
@@ -569,11 +792,6 @@ const SSEInterceptor = {
569
792
  openEventCallback(event, this);
570
793
  }
571
794
  });
572
- this.addEventListener("message", (event) => {
573
- if (messageCallback) {
574
- messageCallback(event, this);
575
- }
576
- });
577
795
  this.addEventListener(
578
796
  "error",
579
797
  (event) => {
@@ -589,6 +807,14 @@ const SSEInterceptor = {
589
807
  });
590
808
  return originalOpen.call(this);
591
809
  };
810
+ eventSourceClass.prototype.dispatch = function(eventType, data) {
811
+ if (!BUILT_IN_EVENT_TYPES.has(eventType)) {
812
+ if (messageCallback) {
813
+ messageCallback(data, this);
814
+ }
815
+ }
816
+ return originalDispatch.call(this, eventType, data);
817
+ };
592
818
  isInterceptorEnabled = true;
593
819
  },
594
820
  // Unpatch EventSource open method and remove the callbacks.
@@ -598,6 +824,7 @@ const SSEInterceptor = {
598
824
  }
599
825
  isInterceptorEnabled = false;
600
826
  eventSourceClass.prototype.open = originalOpen;
827
+ eventSourceClass.prototype.dispatch = originalDispatch;
601
828
  connectCallback = null;
602
829
  messageCallback = null;
603
830
  errorCallback = null;
@@ -611,9 +838,7 @@ const getSSEInspector = () => {
611
838
  var _a;
612
839
  const requestId = (_a = eventSource2._xhr) == null ? void 0 : _a._rozeniteRequestId;
613
840
  if (!requestId) {
614
- throw new Error(
615
- "No request ID found for EventSource. This should never happen!"
616
- );
841
+ return null;
617
842
  }
618
843
  return requestId;
619
844
  };
@@ -622,6 +847,9 @@ const getSSEInspector = () => {
622
847
  SSEInterceptor.setOpenEventCallback((_, eventSource2) => {
623
848
  const sseEventSource = eventSource2;
624
849
  const requestId = getRequestId(sseEventSource);
850
+ if (!requestId) {
851
+ return;
852
+ }
625
853
  const sseXhr = sseEventSource._xhr;
626
854
  const event = {
627
855
  type: "sse-open",
@@ -642,17 +870,26 @@ const getSSEInspector = () => {
642
870
  SSEInterceptor.setMessageCallback((messageEvent, eventSource2) => {
643
871
  const sseEventSource = eventSource2;
644
872
  const requestId = getRequestId(sseEventSource);
873
+ if (!requestId) {
874
+ return;
875
+ }
645
876
  const event = {
646
877
  type: "sse-message",
647
878
  requestId,
648
879
  timestamp: Date.now(),
649
- data: messageEvent.data || ""
880
+ payload: {
881
+ type: messageEvent.type,
882
+ data: messageEvent.data || ""
883
+ }
650
884
  };
651
885
  eventEmitter.emit("sse-message", event);
652
886
  });
653
887
  SSEInterceptor.setErrorCallback((errorEvent, eventSource2) => {
654
888
  const sseEventSource = eventSource2;
655
889
  const requestId = getRequestId(sseEventSource);
890
+ if (!requestId) {
891
+ return;
892
+ }
656
893
  const event = {
657
894
  type: "sse-error",
658
895
  requestId,
@@ -667,6 +904,9 @@ const getSSEInspector = () => {
667
904
  SSEInterceptor.setCloseCallback((_, eventSource2) => {
668
905
  const sseEventSource = eventSource2;
669
906
  const requestId = getRequestId(sseEventSource);
907
+ if (!requestId) {
908
+ return;
909
+ }
670
910
  const event = {
671
911
  type: "sse-close",
672
912
  requestId,
@@ -681,26 +921,93 @@ const getSSEInspector = () => {
681
921
  },
682
922
  isEnabled: () => SSEInterceptor.isInterceptorEnabled(),
683
923
  dispose: () => {
924
+ SSEInterceptor.disableInterception();
684
925
  eventEmitter.events = {};
685
926
  },
686
927
  on: (event, callback) => eventEmitter.on(event, callback)
687
928
  };
688
929
  };
689
- const useNetworkActivityDevTools = () => {
930
+ const DEFAULT_CONFIG = {
931
+ inspectors: {
932
+ http: true,
933
+ websocket: true,
934
+ sse: true
935
+ },
936
+ clientUISettings: {
937
+ showUrlAsName: false
938
+ }
939
+ };
940
+ const validateConfig = (config) => {
941
+ const inspectors = config.inspectors;
942
+ if (!inspectors) {
943
+ return;
944
+ }
945
+ if (inspectors.sse && !inspectors.http) {
946
+ throw new Error("SSE inspector requires HTTP inspector to be enabled.");
947
+ }
948
+ };
949
+ const overridesRegistry = getOverridesRegistry();
950
+ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
951
+ var _a, _b, _c, _d;
952
+ const isRecordingEnabledRef = react.useRef(false);
690
953
  const client = pluginBridge.useRozeniteDevToolsClient({
691
954
  pluginId: "@rozenite/network-activity-plugin"
692
955
  });
956
+ const isHttpInspectorEnabled = ((_a = config.inspectors) == null ? void 0 : _a.http) ?? true;
957
+ const isWebSocketInspectorEnabled = ((_b = config.inspectors) == null ? void 0 : _b.websocket) ?? true;
958
+ const isSSEInspectorEnabled = ((_c = config.inspectors) == null ? void 0 : _c.sse) ?? true;
959
+ const showUrlAsName = (_d = config.clientUISettings) == null ? void 0 : _d.showUrlAsName;
693
960
  react.useEffect(() => {
694
961
  if (!client) {
695
962
  return;
696
963
  }
964
+ validateConfig(config);
965
+ }, [config]);
966
+ react.useEffect(() => {
967
+ if (!client) {
968
+ return;
969
+ }
970
+ const sendClientUISettings = () => {
971
+ var _a2;
972
+ client.send("client-ui-settings", {
973
+ settings: {
974
+ showUrlAsName: showUrlAsName ?? ((_a2 = DEFAULT_CONFIG.clientUISettings) == null ? void 0 : _a2.showUrlAsName)
975
+ }
976
+ });
977
+ };
978
+ const subscriptions = [
979
+ client.onMessage("network-enable", () => {
980
+ isRecordingEnabledRef.current = true;
981
+ }),
982
+ client.onMessage("network-disable", () => {
983
+ isRecordingEnabledRef.current = false;
984
+ }),
985
+ client.onMessage("set-overrides", (data) => {
986
+ overridesRegistry.setOverrides(data.overrides);
987
+ }),
988
+ client.onMessage("get-client-ui-settings", () => {
989
+ sendClientUISettings();
990
+ })
991
+ ];
992
+ sendClientUISettings();
993
+ return () => {
994
+ subscriptions.forEach((subscription) => subscription.remove());
995
+ };
996
+ }, [client, showUrlAsName]);
997
+ react.useEffect(() => {
998
+ if (!client || !isHttpInspectorEnabled) {
999
+ return;
1000
+ }
697
1001
  const networkInspector = getNetworkInspector(client);
1002
+ if (isRecordingEnabledRef.current) {
1003
+ networkInspector.enable();
1004
+ }
698
1005
  return () => {
699
1006
  networkInspector.dispose();
700
1007
  };
701
- }, [client]);
1008
+ }, [client, isHttpInspectorEnabled]);
702
1009
  react.useEffect(() => {
703
- if (!client) {
1010
+ if (!client || !isWebSocketInspectorEnabled) {
704
1011
  return;
705
1012
  }
706
1013
  const eventsToForward = [
@@ -724,12 +1031,15 @@ const useNetworkActivityDevTools = () => {
724
1031
  client.onMessage("network-disable", () => {
725
1032
  websocketInspector.disable();
726
1033
  });
1034
+ if (isRecordingEnabledRef.current) {
1035
+ websocketInspector.enable();
1036
+ }
727
1037
  return () => {
728
1038
  websocketInspector.dispose();
729
1039
  };
730
- }, [client]);
1040
+ }, [client, isWebSocketInspectorEnabled]);
731
1041
  react.useEffect(() => {
732
- if (!client) {
1042
+ if (!client || !isSSEInspectorEnabled) {
733
1043
  return;
734
1044
  }
735
1045
  const eventsToForward = [
@@ -750,10 +1060,13 @@ const useNetworkActivityDevTools = () => {
750
1060
  client.onMessage("network-disable", () => {
751
1061
  sseInspector.disable();
752
1062
  });
1063
+ if (isRecordingEnabledRef.current) {
1064
+ sseInspector.enable();
1065
+ }
753
1066
  return () => {
754
1067
  sseInspector.dispose();
755
1068
  };
756
- }, [client]);
1069
+ }, [client, isSSEInspectorEnabled]);
757
1070
  return client;
758
1071
  };
759
1072
  exports.useNetworkActivityDevTools = useNetworkActivityDevTools;