@rozenite/network-activity-plugin 1.0.0-alpha.8 → 1.0.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 (160) hide show
  1. package/README.md +2 -0
  2. package/dist/App.html +2 -2
  3. package/dist/assets/{App-R2ZMH9wJ.css → App-BrSkOkws.css} +269 -2
  4. package/dist/assets/{App-lNMijPJ4.js → App-C6wCDVkW.js} +17485 -10814
  5. package/dist/event-source.cjs +22 -0
  6. package/dist/event-source.js +23 -0
  7. package/dist/react-native.cjs +4 -1
  8. package/dist/react-native.js +4 -1
  9. package/dist/rozenite.json +1 -1
  10. package/dist/src/react-native/config.d.ts +20 -0
  11. package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
  12. package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
  13. package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +7 -1
  14. package/dist/src/react-native/sse/event-source.d.ts +2 -0
  15. package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
  16. package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
  17. package/dist/src/react-native/sse/types.d.ts +6 -0
  18. package/dist/src/react-native/useNetworkActivityDevTools.d.ts +2 -1
  19. package/dist/src/react-native/utils/getBlobName.d.ts +35 -0
  20. package/dist/src/react-native/utils/getFormDataEntries.d.ts +18 -0
  21. package/dist/src/react-native/utils.d.ts +6 -0
  22. package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
  23. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
  24. package/dist/src/shared/client.d.ts +53 -6
  25. package/dist/src/shared/sse-events.d.ts +38 -0
  26. package/dist/src/shared/websocket-events.d.ts +60 -0
  27. package/dist/src/ui/components/Badge.d.ts +1 -1
  28. package/dist/src/ui/components/Button.d.ts +2 -2
  29. package/dist/src/ui/components/CodeBlock.d.ts +3 -0
  30. package/dist/src/ui/components/CodeEditor.d.ts +5 -0
  31. package/dist/src/ui/components/CookieCard.d.ts +7 -0
  32. package/dist/src/ui/components/CopyRequestDropdown.d.ts +7 -0
  33. package/dist/src/ui/components/DropdownMenu.d.ts +27 -0
  34. package/dist/src/ui/components/FilterBar.d.ts +10 -0
  35. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
  36. package/dist/src/ui/components/KeyValueGrid.d.ts +13 -0
  37. package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
  38. package/dist/src/ui/components/RequestBody.d.ts +6 -0
  39. package/dist/src/ui/components/RequestList.d.ts +13 -28
  40. package/dist/src/ui/components/ScrollArea.d.ts +3 -2
  41. package/dist/src/ui/components/Section.d.ts +8 -0
  42. package/dist/src/ui/components/Separator.d.ts +2 -1
  43. package/dist/src/ui/components/SidePanel.d.ts +1 -0
  44. package/dist/src/ui/components/Tabs.d.ts +7 -0
  45. package/dist/src/ui/components/Toolbar.d.ts +1 -0
  46. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
  47. package/dist/src/ui/state/derived.d.ts +5 -0
  48. package/dist/src/ui/state/hooks.d.ts +21 -0
  49. package/dist/src/ui/state/model.d.ts +103 -0
  50. package/dist/src/ui/state/store.d.ts +48 -0
  51. package/dist/src/ui/tabs/CookiesTab.d.ts +3 -6
  52. package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
  53. package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
  54. package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
  55. package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
  56. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
  57. package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
  58. package/dist/src/ui/types.d.ts +4 -1
  59. package/dist/src/ui/utils/assert.d.ts +1 -0
  60. package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +2 -0
  61. package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
  62. package/dist/src/ui/utils/escapeShellArg.d.ts +1 -0
  63. package/dist/src/ui/utils/generateCurlCommand.d.ts +2 -0
  64. package/dist/src/ui/utils/generateFetchCall.d.ts +2 -0
  65. package/dist/src/ui/utils/generateMultipartBody.d.ts +4 -0
  66. package/dist/src/ui/utils/getId.d.ts +1 -0
  67. package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
  68. package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +7 -0
  69. package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +9 -0
  70. package/dist/src/utils/cookieParser.d.ts +6 -0
  71. package/dist/src/utils/getContentTypeMimeType.d.ts +2 -0
  72. package/dist/src/utils/getHttpHeader.d.ts +5 -0
  73. package/dist/src/utils/getHttpHeaderValueAsString.d.ts +11 -0
  74. package/dist/src/utils/getStringSizeInBytes.d.ts +1 -0
  75. package/dist/src/utils/inferContentTypeFromPostData.d.ts +2 -0
  76. package/dist/src/utils/safeStringify.d.ts +1 -0
  77. package/dist/src/utils/typeChecks.d.ts +9 -0
  78. package/dist/useNetworkActivityDevTools.cjs +724 -40
  79. package/dist/useNetworkActivityDevTools.js +723 -41
  80. package/package.json +22 -8
  81. package/react-native.ts +6 -1
  82. package/src/react-native/config.ts +43 -0
  83. package/src/react-native/http/network-inspector.ts +388 -0
  84. package/src/react-native/http/overrides-registry.ts +32 -0
  85. package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +19 -2
  86. package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -0
  87. package/src/react-native/sse/event-source.ts +25 -0
  88. package/src/react-native/sse/sse-inspector.ts +139 -0
  89. package/src/react-native/sse/sse-interceptor.ts +180 -0
  90. package/src/react-native/sse/types.ts +9 -0
  91. package/src/react-native/useNetworkActivityDevTools.ts +156 -4
  92. package/src/react-native/utils/getBlobName.ts +45 -0
  93. package/src/react-native/utils/getFormDataEntries.ts +32 -0
  94. package/src/react-native/utils.ts +43 -0
  95. package/src/react-native/websocket/websocket-inspector.ts +180 -0
  96. package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
  97. package/src/react-native/websocket/websocket-interceptor.ts +166 -0
  98. package/src/shared/client.ts +79 -6
  99. package/src/shared/sse-events.ts +47 -0
  100. package/src/shared/websocket-events.ts +79 -0
  101. package/src/ui/components/Button.tsx +1 -0
  102. package/src/ui/components/CodeBlock.tsx +19 -0
  103. package/src/ui/components/CodeEditor.tsx +26 -0
  104. package/src/ui/components/CookieCard.tsx +64 -0
  105. package/src/ui/components/CopyRequestDropdown.tsx +95 -0
  106. package/src/ui/components/DropdownMenu.tsx +206 -0
  107. package/src/ui/components/FilterBar.tsx +117 -0
  108. package/src/ui/components/Input.tsx +1 -1
  109. package/src/ui/components/JsonTree.tsx +20 -0
  110. package/src/ui/components/JsonTreeCopyableItem.tsx +37 -0
  111. package/src/ui/components/KeyValueGrid.tsx +51 -0
  112. package/src/ui/components/OverrideResponse.tsx +132 -0
  113. package/src/ui/components/RequestBody.tsx +86 -0
  114. package/src/ui/components/RequestList.tsx +101 -131
  115. package/src/ui/components/ScrollArea.tsx +1 -0
  116. package/src/ui/components/Section.tsx +46 -0
  117. package/src/ui/components/SidePanel.tsx +333 -0
  118. package/src/ui/components/Tabs.tsx +1 -1
  119. package/src/ui/components/Toolbar.tsx +45 -0
  120. package/src/ui/globals.css +4 -0
  121. package/src/ui/hooks/useCopyToClipboard.ts +28 -0
  122. package/src/ui/state/derived.ts +112 -0
  123. package/src/ui/state/hooks.ts +52 -0
  124. package/src/ui/state/model.ts +140 -0
  125. package/src/ui/state/store.ts +669 -0
  126. package/src/ui/tabs/CookiesTab.tsx +61 -278
  127. package/src/ui/tabs/HeadersTab.tsx +85 -103
  128. package/src/ui/tabs/MessagesTab.tsx +276 -0
  129. package/src/ui/tabs/RequestTab.tsx +58 -51
  130. package/src/ui/tabs/ResponseTab.tsx +101 -74
  131. package/src/ui/tabs/SSEMessagesTab.tsx +224 -0
  132. package/src/ui/tabs/TimingTab.tsx +30 -43
  133. package/src/ui/types.ts +4 -1
  134. package/src/ui/utils/assert.ts +5 -0
  135. package/src/ui/utils/checkRequestBodyBinary.ts +7 -0
  136. package/src/ui/utils/copyToClipboard.ts +3 -0
  137. package/src/ui/utils/escapeShellArg.ts +12 -0
  138. package/src/ui/utils/generateCurlCommand.ts +83 -0
  139. package/src/ui/utils/generateFetchCall.ts +64 -0
  140. package/src/ui/utils/generateMultipartBody.ts +19 -0
  141. package/src/ui/utils/getId.ts +10 -0
  142. package/src/ui/utils/getStatusColor.ts +15 -0
  143. package/src/ui/views/InspectorView.tsx +35 -319
  144. package/src/utils/applyReactNativeRequestHeadersLogic.ts +30 -0
  145. package/src/utils/applyReactNativeResponseHeadersLogic.ts +28 -0
  146. package/src/utils/cookieParser.ts +126 -0
  147. package/src/utils/getContentTypeMimeType.ts +17 -0
  148. package/src/utils/getHttpHeader.ts +17 -0
  149. package/src/utils/getHttpHeaderValueAsString.ts +13 -0
  150. package/src/utils/getStringSizeInBytes.ts +3 -0
  151. package/src/utils/inferContentTypeFromPostData.ts +9 -0
  152. package/src/utils/safeStringify.ts +7 -0
  153. package/src/utils/typeChecks.ts +27 -0
  154. package/tailwind.config.ts +3 -0
  155. package/vite.config.ts +12 -0
  156. package/dist/src/ui/utils/getHttpHeaderValue.d.ts +0 -2
  157. package/src/react-native/network-inspector.ts +0 -247
  158. package/src/ui/utils/getHttpHeaderValue.ts +0 -14
  159. /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
  160. /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
@@ -1,14 +1,54 @@
1
- import { useEffect } from "react";
1
+ import { useRef, useEffect } from "react";
2
2
  import { useRozeniteDevToolsClient } from "@rozenite/plugin-bridge";
3
- function getHttpHeaderValue(headers, name) {
3
+ import { createNanoEvents } from "nanoevents";
4
+ import { Platform } from "react-native";
5
+ import WebSocketInterceptor from "react-native/Libraries/WebSocket/WebSocketInterceptor";
6
+ import { g as getEventSource } from "./event-source.js";
7
+ function safeStringify(data) {
8
+ try {
9
+ return typeof data === "string" ? data : JSON.stringify(data);
10
+ } catch {
11
+ return String(data);
12
+ }
13
+ }
14
+ function getHttpHeader(headers, name) {
4
15
  const lowerName = name.toLowerCase();
5
16
  for (const key in headers) {
6
17
  if (key.toLowerCase() === lowerName) {
7
- return headers[key];
18
+ return { value: headers[key], originalKey: key };
8
19
  }
9
20
  }
10
21
  return void 0;
11
22
  }
23
+ function getContentTypeMime(headers) {
24
+ const contentType = getHttpHeader(headers, "content-type");
25
+ if (!contentType) {
26
+ return void 0;
27
+ }
28
+ const { value } = contentType;
29
+ const actualValue = Array.isArray(value) ? value[0] : value;
30
+ return actualValue.split(";")[0].trim();
31
+ }
32
+ const getContentType = (request) => {
33
+ const responseHeaders = request.responseHeaders;
34
+ const responseType = request.responseType;
35
+ const contentType = getContentTypeMime(responseHeaders || {});
36
+ if (contentType) {
37
+ return contentType;
38
+ }
39
+ switch (responseType) {
40
+ case "arraybuffer":
41
+ case "blob":
42
+ return "application/octet-stream";
43
+ case "text":
44
+ case "":
45
+ return "text/plain";
46
+ case "json":
47
+ return "application/json";
48
+ case "document":
49
+ return "text/html";
50
+ }
51
+ };
12
52
  const REQUEST_TTL = 1e3 * 60 * 5;
13
53
  const getNetworkRequestsRegistry = () => {
14
54
  const registry = /* @__PURE__ */ new Map();
@@ -42,6 +82,28 @@ const getNetworkRequestsRegistry = () => {
42
82
  clear
43
83
  };
44
84
  };
85
+ function getBlobName(blob) {
86
+ if (typeof (blob == null ? void 0 : blob.name) === "string") {
87
+ return blob.name;
88
+ }
89
+ if ((blob == null ? void 0 : blob.data) && typeof blob.data.name === "string") {
90
+ return blob.data.name;
91
+ }
92
+ return void 0;
93
+ }
94
+ function getFormDataEntries(formData) {
95
+ if (!formData || typeof formData !== "object") {
96
+ return [];
97
+ }
98
+ if (typeof formData.entries === "function") {
99
+ return formData.entries();
100
+ }
101
+ if (Array.isArray(formData._parts)) {
102
+ return formData._parts;
103
+ }
104
+ return [];
105
+ }
106
+ const XMLHttpRequest = global.XMLHttpRequest || window.XMLHttpRequest;
45
107
  const originalXHROpen = XMLHttpRequest.prototype.open;
46
108
  const originalXHRSend = XMLHttpRequest.prototype.send;
47
109
  const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
@@ -50,7 +112,8 @@ let sendCallback;
50
112
  let requestHeaderCallback;
51
113
  let headerReceivedCallback;
52
114
  let responseCallback;
53
- let isInterceptorEnabled = false;
115
+ let overrideCallback;
116
+ let isInterceptorEnabled$1 = false;
54
117
  const XHRInterceptor = {
55
118
  /**
56
119
  * Invoked before XMLHttpRequest.open(...) is called.
@@ -82,11 +145,17 @@ const XHRInterceptor = {
82
145
  setRequestHeaderCallback(callback) {
83
146
  requestHeaderCallback = callback;
84
147
  },
148
+ /**
149
+ * Invoked before XMLHttpRequest.send(...) is called.
150
+ */
151
+ setOverrideCallback(callback) {
152
+ overrideCallback = callback;
153
+ },
85
154
  isInterceptorEnabled() {
86
- return isInterceptorEnabled;
155
+ return isInterceptorEnabled$1;
87
156
  },
88
157
  enableInterception() {
89
- if (isInterceptorEnabled) {
158
+ if (isInterceptorEnabled$1) {
90
159
  return;
91
160
  }
92
161
  XMLHttpRequest.prototype.open = function(method, url) {
@@ -105,11 +174,14 @@ const XHRInterceptor = {
105
174
  if (sendCallback) {
106
175
  sendCallback(data, this);
107
176
  }
177
+ if (overrideCallback) {
178
+ overrideCallback(this);
179
+ }
108
180
  if (this.addEventListener) {
109
181
  this.addEventListener(
110
182
  "readystatechange",
111
183
  () => {
112
- if (!isInterceptorEnabled) {
184
+ if (!isInterceptorEnabled$1) {
113
185
  return;
114
186
  }
115
187
  if (this.readyState === this.HEADERS_RECEIVED) {
@@ -149,14 +221,14 @@ const XHRInterceptor = {
149
221
  }
150
222
  originalXHRSend.apply(this, arguments);
151
223
  };
152
- isInterceptorEnabled = true;
224
+ isInterceptorEnabled$1 = true;
153
225
  },
154
226
  // Unpatch XMLHttpRequest methods and remove the callbacks.
155
227
  disableInterception() {
156
- if (!isInterceptorEnabled) {
228
+ if (!isInterceptorEnabled$1) {
157
229
  return;
158
230
  }
159
- isInterceptorEnabled = false;
231
+ isInterceptorEnabled$1 = false;
160
232
  XMLHttpRequest.prototype.send = originalXHRSend;
161
233
  XMLHttpRequest.prototype.open = originalXHROpen;
162
234
  XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
@@ -165,38 +237,132 @@ const XHRInterceptor = {
165
237
  sendCallback = null;
166
238
  headerReceivedCallback = null;
167
239
  requestHeaderCallback = null;
240
+ overrideCallback = null;
168
241
  }
169
242
  };
243
+ const getStringSizeInBytes = (value) => {
244
+ return new TextEncoder().encode(value).length;
245
+ };
246
+ const splitSetCookieHeaderByComma = (header) => {
247
+ const regex = /(?:^|,\s)([^=;,]+=[^;]+(?:;[^,]*)*)/g;
248
+ const matches = [];
249
+ let match;
250
+ while ((match = regex.exec(header)) !== null) {
251
+ matches.push(match[1].trim());
252
+ }
253
+ return matches;
254
+ };
255
+ const applyReactNativeResponseHeadersLogic = (headers) => {
256
+ const parsedHeaders = { ...headers };
257
+ const setCookieHeader = getHttpHeader(headers, "set-cookie");
258
+ if (setCookieHeader) {
259
+ const { value, originalKey } = setCookieHeader;
260
+ const cookies = splitSetCookieHeaderByComma(value);
261
+ parsedHeaders[originalKey] = cookies.length > 0 ? cookies : value;
262
+ }
263
+ return parsedHeaders;
264
+ };
265
+ const isBlob = (value) => value instanceof Blob;
266
+ const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
267
+ const isFormData = (value) => value instanceof FormData;
268
+ const isNullOrUndefined = (value) => value === null || value === void 0;
269
+ const createOverridesRegistry = () => {
270
+ let overrides = /* @__PURE__ */ new Map();
271
+ const setOverrides = (newOverrides) => {
272
+ overrides = new Map(newOverrides);
273
+ };
274
+ const getOverrideForUrl = (url) => {
275
+ return overrides.get(url);
276
+ };
277
+ return {
278
+ setOverrides,
279
+ getOverrideForUrl
280
+ };
281
+ };
282
+ let registryInstance = null;
283
+ const getOverridesRegistry = () => {
284
+ if (!registryInstance) {
285
+ registryInstance = createOverridesRegistry();
286
+ }
287
+ return registryInstance;
288
+ };
170
289
  const networkRequestsRegistry = getNetworkRequestsRegistry();
171
- const getContentType = (request) => {
172
- const responseHeaders = request.responseHeaders;
173
- const responseType = request.responseType;
174
- const contentType = getHttpHeaderValue(responseHeaders || {}, "content-type");
175
- if (contentType) {
176
- return contentType.split(";")[0].trim();
290
+ const overridesRegistry$1 = getOverridesRegistry();
291
+ const getBinaryPostData = (body) => ({
292
+ type: "binary",
293
+ value: {
294
+ size: body.size,
295
+ type: body.type,
296
+ name: getBlobName(body)
177
297
  }
178
- switch (responseType) {
179
- case "arraybuffer":
180
- case "blob":
181
- return "application/octet-stream";
182
- case "text":
183
- case "":
184
- return "text/plain";
185
- case "json":
186
- return "application/json";
187
- case "document":
188
- return "text/html";
298
+ });
299
+ const getArrayBufferPostData = (body) => ({
300
+ type: "binary",
301
+ value: {
302
+ size: body.byteLength
303
+ }
304
+ });
305
+ const getTextPostData = (body) => ({
306
+ type: "text",
307
+ value: safeStringify(body)
308
+ });
309
+ const getFormDataPostData = (body) => ({
310
+ type: "form-data",
311
+ value: getFormDataEntries(body).reduce(
312
+ (acc, [key, value]) => {
313
+ if (isBlob(value)) {
314
+ acc[key] = getBinaryPostData(value);
315
+ } else if (isArrayBuffer(value)) {
316
+ acc[key] = getArrayBufferPostData(value);
317
+ } else {
318
+ acc[key] = getTextPostData(value);
319
+ }
320
+ return acc;
321
+ },
322
+ {}
323
+ )
324
+ });
325
+ const getRequestBody = (body) => {
326
+ if (isNullOrUndefined(body)) {
327
+ return body;
328
+ }
329
+ if (isBlob(body)) {
330
+ return getBinaryPostData(body);
189
331
  }
332
+ if (isArrayBuffer(body)) {
333
+ return getArrayBufferPostData(body);
334
+ }
335
+ if (isFormData(body)) {
336
+ return getFormDataPostData(body);
337
+ }
338
+ return getTextPostData(body);
190
339
  };
191
340
  const getResponseSize = (request) => {
192
- if (typeof request.response === "object") {
193
- return request.response.size;
341
+ try {
342
+ const { responseType, response } = request;
343
+ if (response === null) {
344
+ return 0;
345
+ }
346
+ if (responseType === "" || responseType === "text") {
347
+ return getStringSizeInBytes(request.responseText);
348
+ }
349
+ if (responseType === "json") {
350
+ return getStringSizeInBytes(safeStringify(response));
351
+ }
352
+ if (responseType === "blob") {
353
+ return response.size;
354
+ }
355
+ if (responseType === "arraybuffer") {
356
+ return response.byteLength;
357
+ }
358
+ return 0;
359
+ } catch {
360
+ return null;
194
361
  }
195
- return request.response.length || 0;
196
362
  };
197
363
  const getResponseBody = async (request) => {
198
364
  const responseType = request.responseType;
199
- if (responseType === "text") {
365
+ if (responseType === "" || responseType === "text") {
200
366
  return request.responseText;
201
367
  }
202
368
  if (responseType === "blob") {
@@ -211,6 +377,9 @@ const getResponseBody = async (request) => {
211
377
  });
212
378
  }
213
379
  }
380
+ if (responseType === "json") {
381
+ return safeStringify(request.response);
382
+ }
214
383
  return null;
215
384
  };
216
385
  const getInitiatorFromStack = () => {
@@ -241,17 +410,18 @@ const getNetworkInspector = (pluginClient) => {
241
410
  const handleRequestSend = (data, request) => {
242
411
  const sendTime = Date.now();
243
412
  const requestId = generateRequestId();
413
+ request._rozeniteRequestId = requestId;
244
414
  const initiator = getInitiatorFromStack();
245
415
  networkRequestsRegistry.addEntry(requestId, request);
246
416
  let ttfb = 0;
247
417
  pluginClient.send("request-sent", {
248
418
  requestId,
249
- timestamp: sendTime / 1e3,
419
+ timestamp: sendTime,
250
420
  request: {
251
421
  url: request._url,
252
422
  method: request._method,
253
423
  headers: request._headers,
254
- postData: data
424
+ postData: getRequestBody(data)
255
425
  },
256
426
  type: "XHR",
257
427
  initiator
@@ -264,23 +434,25 @@ const getNetworkInspector = (pluginClient) => {
264
434
  request.addEventListener("load", () => {
265
435
  pluginClient.send("response-received", {
266
436
  requestId,
267
- timestamp: Date.now() / 1e3,
437
+ timestamp: Date.now(),
268
438
  type: "XHR",
269
439
  response: {
270
440
  url: request._url,
271
441
  status: request.status,
272
442
  statusText: request.statusText,
273
- headers: request.responseHeaders || {},
443
+ headers: applyReactNativeResponseHeadersLogic(
444
+ request.responseHeaders || {}
445
+ ),
274
446
  contentType: getContentType(request),
275
447
  size: getResponseSize(request),
276
- responseTime: Date.now() / 1e3
448
+ responseTime: Date.now()
277
449
  }
278
450
  });
279
451
  });
280
452
  request.addEventListener("loadend", () => {
281
453
  pluginClient.send("request-completed", {
282
454
  requestId,
283
- timestamp: Date.now() / 1e3,
455
+ timestamp: Date.now(),
284
456
  duration: Date.now() - sendTime,
285
457
  size: getResponseSize(request),
286
458
  ttfb
@@ -289,7 +461,7 @@ const getNetworkInspector = (pluginClient) => {
289
461
  request.addEventListener("error", () => {
290
462
  pluginClient.send("request-failed", {
291
463
  requestId,
292
- timestamp: Date.now() / 1e3,
464
+ timestamp: Date.now(),
293
465
  type: "XHR",
294
466
  error: "Failed",
295
467
  canceled: false
@@ -298,16 +470,52 @@ const getNetworkInspector = (pluginClient) => {
298
470
  request.addEventListener("abort", () => {
299
471
  pluginClient.send("request-failed", {
300
472
  requestId,
301
- timestamp: Date.now() / 1e3,
473
+ timestamp: Date.now(),
302
474
  type: "XHR",
303
475
  error: "Aborted",
304
476
  canceled: true
305
477
  });
306
478
  });
307
479
  };
480
+ const handleRequestOverride = (request) => {
481
+ const override = overridesRegistry$1.getOverrideForUrl(
482
+ request._url
483
+ );
484
+ if (!override) {
485
+ return;
486
+ }
487
+ request.addEventListener("readystatechange", () => {
488
+ if (override.body !== void 0) {
489
+ Object.defineProperty(request, "responseType", {
490
+ writable: true
491
+ });
492
+ Object.defineProperty(request, "response", {
493
+ writable: true
494
+ });
495
+ Object.defineProperty(request, "responseText", {
496
+ writable: true
497
+ });
498
+ const contentType = getContentType(request);
499
+ if (contentType === "application/json") {
500
+ request.responseType = "json";
501
+ } else if (contentType === "text/plain") {
502
+ request.responseType = "text";
503
+ }
504
+ request.response = override.body;
505
+ request.responseText = override.body;
506
+ }
507
+ if (override.status !== void 0) {
508
+ Object.defineProperty(request, "status", {
509
+ writable: true
510
+ });
511
+ request.status = override.status;
512
+ }
513
+ });
514
+ };
308
515
  const enable = () => {
309
516
  XHRInterceptor.disableInterception();
310
517
  XHRInterceptor.setSendCallback(handleRequestSend);
518
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
311
519
  XHRInterceptor.enableInterception();
312
520
  };
313
521
  const disable = () => {
@@ -350,19 +558,493 @@ const getNetworkInspector = (pluginClient) => {
350
558
  dispose
351
559
  };
352
560
  };
353
- const useNetworkActivityDevTools = () => {
561
+ const getWebSocketInterceptor = () => {
562
+ if (Platform.constants.reactNativeVersion.minor >= 79) {
563
+ return WebSocketInterceptor;
564
+ } else {
565
+ const WebSocketInterceptorPreRN079 = WebSocketInterceptor;
566
+ return {
567
+ ...WebSocketInterceptorPreRN079,
568
+ setOnMessageCallback: (callback) => {
569
+ WebSocketInterceptorPreRN079.setOnMessageCallback((socketId, data) => {
570
+ callback(data, socketId);
571
+ });
572
+ },
573
+ setOnCloseCallback: (callback) => {
574
+ WebSocketInterceptorPreRN079.setOnCloseCallback((error, socketId) => {
575
+ callback(socketId, error);
576
+ });
577
+ },
578
+ setOnErrorCallback: (callback) => {
579
+ WebSocketInterceptorPreRN079.setOnErrorCallback((error, socketId) => {
580
+ callback(socketId, error);
581
+ });
582
+ }
583
+ };
584
+ }
585
+ };
586
+ const getWebSocketInspector = () => {
587
+ const eventEmitter = createNanoEvents();
588
+ const socketUrlMap = /* @__PURE__ */ new Map();
589
+ const webSocketInterceptor = getWebSocketInterceptor();
590
+ return {
591
+ enable: () => {
592
+ webSocketInterceptor.setConnectCallback(
593
+ (url, protocols, options, socketId) => {
594
+ socketUrlMap.set(socketId, url);
595
+ const event = {
596
+ type: "websocket-connect",
597
+ url,
598
+ socketId,
599
+ timestamp: Date.now(),
600
+ protocols,
601
+ options
602
+ };
603
+ eventEmitter.emit("websocket-connect", event);
604
+ }
605
+ );
606
+ webSocketInterceptor.setCloseCallback(
607
+ (code, reason, socketId) => {
608
+ const url = socketUrlMap.get(socketId);
609
+ if (!url) {
610
+ return;
611
+ }
612
+ const event = {
613
+ type: "websocket-close",
614
+ url,
615
+ socketId,
616
+ timestamp: Date.now(),
617
+ code: code || 0,
618
+ reason: reason || void 0
619
+ };
620
+ eventEmitter.emit("websocket-close", event);
621
+ socketUrlMap.delete(socketId);
622
+ }
623
+ );
624
+ webSocketInterceptor.setOnMessageCallback(
625
+ (data, socketId) => {
626
+ const url = socketUrlMap.get(socketId);
627
+ if (!url) {
628
+ return;
629
+ }
630
+ const event = {
631
+ type: "websocket-message-received",
632
+ url,
633
+ socketId,
634
+ timestamp: Date.now(),
635
+ data,
636
+ messageType: typeof data === "string" ? "text" : "binary"
637
+ };
638
+ eventEmitter.emit("websocket-message-received", event);
639
+ }
640
+ );
641
+ webSocketInterceptor.setOnErrorCallback(
642
+ (error, socketId) => {
643
+ const url = socketUrlMap.get(socketId);
644
+ if (!url) {
645
+ return;
646
+ }
647
+ const event = {
648
+ type: "websocket-error",
649
+ url,
650
+ socketId,
651
+ timestamp: Date.now(),
652
+ error
653
+ };
654
+ eventEmitter.emit("websocket-error", event);
655
+ }
656
+ );
657
+ webSocketInterceptor.setSendCallback((data, socketId) => {
658
+ const url = socketUrlMap.get(socketId);
659
+ if (!url) {
660
+ return;
661
+ }
662
+ const event = {
663
+ type: "websocket-message-sent",
664
+ url,
665
+ socketId,
666
+ timestamp: Date.now(),
667
+ data,
668
+ messageType: typeof data === "string" ? "text" : "binary"
669
+ };
670
+ eventEmitter.emit("websocket-message-sent", event);
671
+ });
672
+ webSocketInterceptor.setOnOpenCallback((socketId) => {
673
+ const url = socketUrlMap.get(socketId);
674
+ if (!url) {
675
+ return;
676
+ }
677
+ const event = {
678
+ type: "websocket-open",
679
+ url,
680
+ socketId,
681
+ timestamp: Date.now()
682
+ };
683
+ eventEmitter.emit("websocket-open", event);
684
+ });
685
+ webSocketInterceptor.setOnCloseCallback(
686
+ (error, socketId) => {
687
+ const url = socketUrlMap.get(socketId);
688
+ if (!url) {
689
+ return;
690
+ }
691
+ const event = {
692
+ type: "websocket-close",
693
+ url,
694
+ socketId,
695
+ timestamp: Date.now(),
696
+ code: error.code,
697
+ reason: error.reason
698
+ };
699
+ eventEmitter.emit("websocket-close", event);
700
+ socketUrlMap.delete(socketId);
701
+ }
702
+ );
703
+ webSocketInterceptor.enableInterception();
704
+ },
705
+ disable: () => {
706
+ webSocketInterceptor.disableInterception();
707
+ },
708
+ isEnabled: () => webSocketInterceptor.isInterceptorEnabled(),
709
+ dispose: () => {
710
+ eventEmitter.events = {};
711
+ socketUrlMap.clear();
712
+ },
713
+ on: (event, callback) => eventEmitter.on(event, callback)
714
+ };
715
+ };
716
+ let connectCallback;
717
+ let messageCallback;
718
+ let errorCallback;
719
+ let openEventCallback;
720
+ let closeCallback;
721
+ let isInterceptorEnabled = false;
722
+ const eventSourceClass = getEventSource();
723
+ const originalOpen = eventSourceClass.prototype.open;
724
+ const originalDispatch = eventSourceClass.prototype.dispatch;
725
+ const BUILT_IN_EVENT_TYPES = /* @__PURE__ */ new Set(["open", "error", "close", "done"]);
726
+ const SSEInterceptor = {
727
+ /**
728
+ * Invoked when EventSource.open() is called (connection attempt starting).
729
+ */
730
+ setConnectCallback(callback) {
731
+ connectCallback = callback;
732
+ },
733
+ /**
734
+ * Invoked when a message event is received.
735
+ */
736
+ setMessageCallback(callback) {
737
+ messageCallback = callback;
738
+ },
739
+ /**
740
+ * Invoked when an error event occurs.
741
+ */
742
+ setErrorCallback(callback) {
743
+ errorCallback = callback;
744
+ },
745
+ /**
746
+ * Invoked when the connection is successfully opened (open event fired).
747
+ */
748
+ setOpenEventCallback(callback) {
749
+ openEventCallback = callback;
750
+ },
751
+ /**
752
+ * Invoked when the connection is closed.
753
+ */
754
+ setCloseCallback(callback) {
755
+ closeCallback = callback;
756
+ },
757
+ isInterceptorEnabled() {
758
+ return isInterceptorEnabled;
759
+ },
760
+ enableInterception() {
761
+ if (isInterceptorEnabled) {
762
+ return;
763
+ }
764
+ eventSourceClass.prototype.open = function() {
765
+ if (connectCallback) {
766
+ connectCallback(this.url, this);
767
+ }
768
+ this.addEventListener("open", (event) => {
769
+ if (openEventCallback) {
770
+ openEventCallback(event, this);
771
+ }
772
+ });
773
+ this.addEventListener(
774
+ "error",
775
+ (event) => {
776
+ if (errorCallback) {
777
+ errorCallback(event, this);
778
+ }
779
+ }
780
+ );
781
+ this.addEventListener("close", (event) => {
782
+ if (closeCallback) {
783
+ closeCallback(event, this);
784
+ }
785
+ });
786
+ return originalOpen.call(this);
787
+ };
788
+ eventSourceClass.prototype.dispatch = function(eventType, data) {
789
+ if (!BUILT_IN_EVENT_TYPES.has(eventType)) {
790
+ if (messageCallback) {
791
+ messageCallback(data, this);
792
+ }
793
+ }
794
+ return originalDispatch.call(this, eventType, data);
795
+ };
796
+ isInterceptorEnabled = true;
797
+ },
798
+ // Unpatch EventSource open method and remove the callbacks.
799
+ disableInterception() {
800
+ if (!isInterceptorEnabled) {
801
+ return;
802
+ }
803
+ isInterceptorEnabled = false;
804
+ eventSourceClass.prototype.open = originalOpen;
805
+ eventSourceClass.prototype.dispatch = originalDispatch;
806
+ connectCallback = null;
807
+ messageCallback = null;
808
+ errorCallback = null;
809
+ openEventCallback = null;
810
+ closeCallback = null;
811
+ }
812
+ };
813
+ const getSSEInspector = () => {
814
+ const eventEmitter = createNanoEvents();
815
+ const getRequestId = (eventSource) => {
816
+ var _a;
817
+ const requestId = (_a = eventSource._xhr) == null ? void 0 : _a._rozeniteRequestId;
818
+ if (!requestId) {
819
+ return null;
820
+ }
821
+ return requestId;
822
+ };
823
+ return {
824
+ enable: () => {
825
+ SSEInterceptor.setOpenEventCallback((_, eventSource) => {
826
+ const sseEventSource = eventSource;
827
+ const requestId = getRequestId(sseEventSource);
828
+ if (!requestId) {
829
+ return;
830
+ }
831
+ const sseXhr = sseEventSource._xhr;
832
+ const event = {
833
+ type: "sse-open",
834
+ requestId,
835
+ timestamp: Date.now(),
836
+ response: {
837
+ url: sseXhr._url,
838
+ status: sseXhr.status,
839
+ statusText: sseXhr.statusText,
840
+ headers: sseXhr.responseHeaders || {},
841
+ contentType: getContentType(sseXhr),
842
+ size: 0,
843
+ responseTime: Date.now()
844
+ }
845
+ };
846
+ eventEmitter.emit("sse-open", event);
847
+ });
848
+ SSEInterceptor.setMessageCallback((messageEvent, eventSource) => {
849
+ const sseEventSource = eventSource;
850
+ const requestId = getRequestId(sseEventSource);
851
+ if (!requestId) {
852
+ return;
853
+ }
854
+ const event = {
855
+ type: "sse-message",
856
+ requestId,
857
+ timestamp: Date.now(),
858
+ payload: {
859
+ type: messageEvent.type,
860
+ data: messageEvent.data || ""
861
+ }
862
+ };
863
+ eventEmitter.emit("sse-message", event);
864
+ });
865
+ SSEInterceptor.setErrorCallback((errorEvent, eventSource) => {
866
+ const sseEventSource = eventSource;
867
+ const requestId = getRequestId(sseEventSource);
868
+ if (!requestId) {
869
+ return;
870
+ }
871
+ const event = {
872
+ type: "sse-error",
873
+ requestId,
874
+ timestamp: Date.now(),
875
+ error: {
876
+ type: errorEvent.type,
877
+ message: errorEvent.type === "timeout" ? "Timeout" : errorEvent.message
878
+ }
879
+ };
880
+ eventEmitter.emit("sse-error", event);
881
+ });
882
+ SSEInterceptor.setCloseCallback((_, eventSource) => {
883
+ const sseEventSource = eventSource;
884
+ const requestId = getRequestId(sseEventSource);
885
+ if (!requestId) {
886
+ return;
887
+ }
888
+ const event = {
889
+ type: "sse-close",
890
+ requestId,
891
+ timestamp: Date.now()
892
+ };
893
+ eventEmitter.emit("sse-close", event);
894
+ });
895
+ SSEInterceptor.enableInterception();
896
+ },
897
+ disable: () => {
898
+ SSEInterceptor.disableInterception();
899
+ },
900
+ isEnabled: () => SSEInterceptor.isInterceptorEnabled(),
901
+ dispose: () => {
902
+ SSEInterceptor.disableInterception();
903
+ eventEmitter.events = {};
904
+ },
905
+ on: (event, callback) => eventEmitter.on(event, callback)
906
+ };
907
+ };
908
+ const DEFAULT_CONFIG = {
909
+ inspectors: {
910
+ http: true,
911
+ websocket: true,
912
+ sse: true
913
+ },
914
+ clientUISettings: {
915
+ showUrlAsName: false
916
+ }
917
+ };
918
+ const validateConfig = (config) => {
919
+ const inspectors = config.inspectors;
920
+ if (!inspectors) {
921
+ return;
922
+ }
923
+ if (inspectors.sse && !inspectors.http) {
924
+ throw new Error("SSE inspector requires HTTP inspector to be enabled.");
925
+ }
926
+ };
927
+ const overridesRegistry = getOverridesRegistry();
928
+ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
929
+ var _a, _b, _c, _d;
930
+ const isRecordingEnabledRef = useRef(false);
354
931
  const client = useRozeniteDevToolsClient({
355
932
  pluginId: "@rozenite/network-activity-plugin"
356
933
  });
934
+ const isHttpInspectorEnabled = ((_a = config.inspectors) == null ? void 0 : _a.http) ?? true;
935
+ const isWebSocketInspectorEnabled = ((_b = config.inspectors) == null ? void 0 : _b.websocket) ?? true;
936
+ const isSSEInspectorEnabled = ((_c = config.inspectors) == null ? void 0 : _c.sse) ?? true;
937
+ const showUrlAsName = (_d = config.clientUISettings) == null ? void 0 : _d.showUrlAsName;
357
938
  useEffect(() => {
358
939
  if (!client) {
359
940
  return;
360
941
  }
942
+ validateConfig(config);
943
+ }, [config]);
944
+ useEffect(() => {
945
+ if (!client) {
946
+ return;
947
+ }
948
+ const sendClientUISettings = () => {
949
+ var _a2;
950
+ client.send("client-ui-settings", {
951
+ settings: {
952
+ showUrlAsName: showUrlAsName ?? ((_a2 = DEFAULT_CONFIG.clientUISettings) == null ? void 0 : _a2.showUrlAsName)
953
+ }
954
+ });
955
+ };
956
+ const subscriptions = [
957
+ client.onMessage("network-enable", () => {
958
+ isRecordingEnabledRef.current = true;
959
+ }),
960
+ client.onMessage("network-disable", () => {
961
+ isRecordingEnabledRef.current = false;
962
+ }),
963
+ client.onMessage("set-overrides", (data) => {
964
+ overridesRegistry.setOverrides(data.overrides);
965
+ }),
966
+ client.onMessage("get-client-ui-settings", () => {
967
+ sendClientUISettings();
968
+ })
969
+ ];
970
+ sendClientUISettings();
971
+ return () => {
972
+ subscriptions.forEach((subscription) => subscription.remove());
973
+ };
974
+ }, [client, showUrlAsName]);
975
+ useEffect(() => {
976
+ if (!client || !isHttpInspectorEnabled) {
977
+ return;
978
+ }
361
979
  const networkInspector = getNetworkInspector(client);
980
+ if (isRecordingEnabledRef.current) {
981
+ networkInspector.enable();
982
+ }
362
983
  return () => {
363
984
  networkInspector.dispose();
364
985
  };
365
- }, [client]);
986
+ }, [client, isHttpInspectorEnabled]);
987
+ useEffect(() => {
988
+ if (!client || !isWebSocketInspectorEnabled) {
989
+ return;
990
+ }
991
+ const eventsToForward = [
992
+ "websocket-connect",
993
+ "websocket-open",
994
+ "websocket-close",
995
+ "websocket-message-sent",
996
+ "websocket-message-received",
997
+ "websocket-error",
998
+ "websocket-connection-status-changed"
999
+ ];
1000
+ const websocketInspector = getWebSocketInspector();
1001
+ eventsToForward.forEach((event) => {
1002
+ websocketInspector.on(event, (event2) => {
1003
+ client.send(event2.type, event2);
1004
+ });
1005
+ });
1006
+ client.onMessage("network-enable", () => {
1007
+ websocketInspector.enable();
1008
+ });
1009
+ client.onMessage("network-disable", () => {
1010
+ websocketInspector.disable();
1011
+ });
1012
+ if (isRecordingEnabledRef.current) {
1013
+ websocketInspector.enable();
1014
+ }
1015
+ return () => {
1016
+ websocketInspector.dispose();
1017
+ };
1018
+ }, [client, isWebSocketInspectorEnabled]);
1019
+ useEffect(() => {
1020
+ if (!client || !isSSEInspectorEnabled) {
1021
+ return;
1022
+ }
1023
+ const eventsToForward = [
1024
+ "sse-open",
1025
+ "sse-message",
1026
+ "sse-error",
1027
+ "sse-close"
1028
+ ];
1029
+ const sseInspector = getSSEInspector();
1030
+ eventsToForward.forEach((event) => {
1031
+ sseInspector.on(event, (event2) => {
1032
+ client.send(event2.type, event2);
1033
+ });
1034
+ });
1035
+ client.onMessage("network-enable", () => {
1036
+ sseInspector.enable();
1037
+ });
1038
+ client.onMessage("network-disable", () => {
1039
+ sseInspector.disable();
1040
+ });
1041
+ if (isRecordingEnabledRef.current) {
1042
+ sseInspector.enable();
1043
+ }
1044
+ return () => {
1045
+ sseInspector.dispose();
1046
+ };
1047
+ }, [client, isSSEInspectorEnabled]);
366
1048
  return client;
367
1049
  };
368
1050
  export {