@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
@@ -2,15 +2,57 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const react = require("react");
4
4
  const pluginBridge = require("@rozenite/plugin-bridge");
5
- function getHttpHeaderValue(headers, name) {
5
+ const nanoevents = require("nanoevents");
6
+ const reactNative = require("react-native");
7
+ const WebSocketInterceptor = require("react-native/Libraries/WebSocket/WebSocketInterceptor");
8
+ const eventSource = require("./event-source.cjs");
9
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
+ const WebSocketInterceptor__default = /* @__PURE__ */ _interopDefault(WebSocketInterceptor);
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) {
6
19
  const lowerName = name.toLowerCase();
7
20
  for (const key in headers) {
8
21
  if (key.toLowerCase() === lowerName) {
9
- return headers[key];
22
+ return { value: headers[key], originalKey: key };
10
23
  }
11
24
  }
12
25
  return void 0;
13
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
+ }
36
+ const getContentType = (request) => {
37
+ const responseHeaders = request.responseHeaders;
38
+ const responseType = request.responseType;
39
+ const contentType = getContentTypeMime(responseHeaders || {});
40
+ if (contentType) {
41
+ return contentType;
42
+ }
43
+ switch (responseType) {
44
+ case "arraybuffer":
45
+ case "blob":
46
+ return "application/octet-stream";
47
+ case "text":
48
+ case "":
49
+ return "text/plain";
50
+ case "json":
51
+ return "application/json";
52
+ case "document":
53
+ return "text/html";
54
+ }
55
+ };
14
56
  const REQUEST_TTL = 1e3 * 60 * 5;
15
57
  const getNetworkRequestsRegistry = () => {
16
58
  const registry = /* @__PURE__ */ new Map();
@@ -44,6 +86,28 @@ const getNetworkRequestsRegistry = () => {
44
86
  clear
45
87
  };
46
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;
47
111
  const originalXHROpen = XMLHttpRequest.prototype.open;
48
112
  const originalXHRSend = XMLHttpRequest.prototype.send;
49
113
  const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
@@ -52,7 +116,8 @@ let sendCallback;
52
116
  let requestHeaderCallback;
53
117
  let headerReceivedCallback;
54
118
  let responseCallback;
55
- let isInterceptorEnabled = false;
119
+ let overrideCallback;
120
+ let isInterceptorEnabled$1 = false;
56
121
  const XHRInterceptor = {
57
122
  /**
58
123
  * Invoked before XMLHttpRequest.open(...) is called.
@@ -84,11 +149,17 @@ const XHRInterceptor = {
84
149
  setRequestHeaderCallback(callback) {
85
150
  requestHeaderCallback = callback;
86
151
  },
152
+ /**
153
+ * Invoked before XMLHttpRequest.send(...) is called.
154
+ */
155
+ setOverrideCallback(callback) {
156
+ overrideCallback = callback;
157
+ },
87
158
  isInterceptorEnabled() {
88
- return isInterceptorEnabled;
159
+ return isInterceptorEnabled$1;
89
160
  },
90
161
  enableInterception() {
91
- if (isInterceptorEnabled) {
162
+ if (isInterceptorEnabled$1) {
92
163
  return;
93
164
  }
94
165
  XMLHttpRequest.prototype.open = function(method, url) {
@@ -107,11 +178,14 @@ const XHRInterceptor = {
107
178
  if (sendCallback) {
108
179
  sendCallback(data, this);
109
180
  }
181
+ if (overrideCallback) {
182
+ overrideCallback(this);
183
+ }
110
184
  if (this.addEventListener) {
111
185
  this.addEventListener(
112
186
  "readystatechange",
113
187
  () => {
114
- if (!isInterceptorEnabled) {
188
+ if (!isInterceptorEnabled$1) {
115
189
  return;
116
190
  }
117
191
  if (this.readyState === this.HEADERS_RECEIVED) {
@@ -151,14 +225,14 @@ const XHRInterceptor = {
151
225
  }
152
226
  originalXHRSend.apply(this, arguments);
153
227
  };
154
- isInterceptorEnabled = true;
228
+ isInterceptorEnabled$1 = true;
155
229
  },
156
230
  // Unpatch XMLHttpRequest methods and remove the callbacks.
157
231
  disableInterception() {
158
- if (!isInterceptorEnabled) {
232
+ if (!isInterceptorEnabled$1) {
159
233
  return;
160
234
  }
161
- isInterceptorEnabled = false;
235
+ isInterceptorEnabled$1 = false;
162
236
  XMLHttpRequest.prototype.send = originalXHRSend;
163
237
  XMLHttpRequest.prototype.open = originalXHROpen;
164
238
  XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
@@ -167,38 +241,132 @@ const XHRInterceptor = {
167
241
  sendCallback = null;
168
242
  headerReceivedCallback = null;
169
243
  requestHeaderCallback = null;
244
+ overrideCallback = null;
170
245
  }
171
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();
290
+ }
291
+ return registryInstance;
292
+ };
172
293
  const networkRequestsRegistry = getNetworkRequestsRegistry();
173
- const getContentType = (request) => {
174
- const responseHeaders = request.responseHeaders;
175
- const responseType = request.responseType;
176
- const contentType = getHttpHeaderValue(responseHeaders || {}, "content-type");
177
- if (contentType) {
178
- return contentType.split(";")[0].trim();
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)
179
301
  }
180
- switch (responseType) {
181
- case "arraybuffer":
182
- case "blob":
183
- return "application/octet-stream";
184
- case "text":
185
- case "":
186
- return "text/plain";
187
- case "json":
188
- return "application/json";
189
- case "document":
190
- return "text/html";
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);
191
335
  }
336
+ if (isArrayBuffer(body)) {
337
+ return getArrayBufferPostData(body);
338
+ }
339
+ if (isFormData(body)) {
340
+ return getFormDataPostData(body);
341
+ }
342
+ return getTextPostData(body);
192
343
  };
193
344
  const getResponseSize = (request) => {
194
- if (typeof request.response === "object") {
195
- 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;
196
365
  }
197
- return request.response.length || 0;
198
366
  };
199
367
  const getResponseBody = async (request) => {
200
368
  const responseType = request.responseType;
201
- if (responseType === "text") {
369
+ if (responseType === "" || responseType === "text") {
202
370
  return request.responseText;
203
371
  }
204
372
  if (responseType === "blob") {
@@ -213,6 +381,9 @@ const getResponseBody = async (request) => {
213
381
  });
214
382
  }
215
383
  }
384
+ if (responseType === "json") {
385
+ return safeStringify(request.response);
386
+ }
216
387
  return null;
217
388
  };
218
389
  const getInitiatorFromStack = () => {
@@ -243,17 +414,18 @@ const getNetworkInspector = (pluginClient) => {
243
414
  const handleRequestSend = (data, request) => {
244
415
  const sendTime = Date.now();
245
416
  const requestId = generateRequestId();
417
+ request._rozeniteRequestId = requestId;
246
418
  const initiator = getInitiatorFromStack();
247
419
  networkRequestsRegistry.addEntry(requestId, request);
248
420
  let ttfb = 0;
249
421
  pluginClient.send("request-sent", {
250
422
  requestId,
251
- timestamp: sendTime / 1e3,
423
+ timestamp: sendTime,
252
424
  request: {
253
425
  url: request._url,
254
426
  method: request._method,
255
427
  headers: request._headers,
256
- postData: data
428
+ postData: getRequestBody(data)
257
429
  },
258
430
  type: "XHR",
259
431
  initiator
@@ -266,23 +438,25 @@ const getNetworkInspector = (pluginClient) => {
266
438
  request.addEventListener("load", () => {
267
439
  pluginClient.send("response-received", {
268
440
  requestId,
269
- timestamp: Date.now() / 1e3,
441
+ timestamp: Date.now(),
270
442
  type: "XHR",
271
443
  response: {
272
444
  url: request._url,
273
445
  status: request.status,
274
446
  statusText: request.statusText,
275
- headers: request.responseHeaders || {},
447
+ headers: applyReactNativeResponseHeadersLogic(
448
+ request.responseHeaders || {}
449
+ ),
276
450
  contentType: getContentType(request),
277
451
  size: getResponseSize(request),
278
- responseTime: Date.now() / 1e3
452
+ responseTime: Date.now()
279
453
  }
280
454
  });
281
455
  });
282
456
  request.addEventListener("loadend", () => {
283
457
  pluginClient.send("request-completed", {
284
458
  requestId,
285
- timestamp: Date.now() / 1e3,
459
+ timestamp: Date.now(),
286
460
  duration: Date.now() - sendTime,
287
461
  size: getResponseSize(request),
288
462
  ttfb
@@ -291,7 +465,7 @@ const getNetworkInspector = (pluginClient) => {
291
465
  request.addEventListener("error", () => {
292
466
  pluginClient.send("request-failed", {
293
467
  requestId,
294
- timestamp: Date.now() / 1e3,
468
+ timestamp: Date.now(),
295
469
  type: "XHR",
296
470
  error: "Failed",
297
471
  canceled: false
@@ -300,16 +474,52 @@ const getNetworkInspector = (pluginClient) => {
300
474
  request.addEventListener("abort", () => {
301
475
  pluginClient.send("request-failed", {
302
476
  requestId,
303
- timestamp: Date.now() / 1e3,
477
+ timestamp: Date.now(),
304
478
  type: "XHR",
305
479
  error: "Aborted",
306
480
  canceled: true
307
481
  });
308
482
  });
309
483
  };
484
+ const handleRequestOverride = (request) => {
485
+ const override = overridesRegistry$1.getOverrideForUrl(
486
+ request._url
487
+ );
488
+ if (!override) {
489
+ return;
490
+ }
491
+ request.addEventListener("readystatechange", () => {
492
+ if (override.body !== void 0) {
493
+ Object.defineProperty(request, "responseType", {
494
+ writable: true
495
+ });
496
+ Object.defineProperty(request, "response", {
497
+ writable: true
498
+ });
499
+ Object.defineProperty(request, "responseText", {
500
+ writable: true
501
+ });
502
+ const contentType = getContentType(request);
503
+ if (contentType === "application/json") {
504
+ request.responseType = "json";
505
+ } else if (contentType === "text/plain") {
506
+ request.responseType = "text";
507
+ }
508
+ request.response = override.body;
509
+ request.responseText = override.body;
510
+ }
511
+ if (override.status !== void 0) {
512
+ Object.defineProperty(request, "status", {
513
+ writable: true
514
+ });
515
+ request.status = override.status;
516
+ }
517
+ });
518
+ };
310
519
  const enable = () => {
311
520
  XHRInterceptor.disableInterception();
312
521
  XHRInterceptor.setSendCallback(handleRequestSend);
522
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
313
523
  XHRInterceptor.enableInterception();
314
524
  };
315
525
  const disable = () => {
@@ -352,19 +562,493 @@ const getNetworkInspector = (pluginClient) => {
352
562
  dispose
353
563
  };
354
564
  };
355
- const useNetworkActivityDevTools = () => {
565
+ const getWebSocketInterceptor = () => {
566
+ if (reactNative.Platform.constants.reactNativeVersion.minor >= 79) {
567
+ return WebSocketInterceptor__default.default;
568
+ } else {
569
+ const WebSocketInterceptorPreRN079 = WebSocketInterceptor__default.default;
570
+ return {
571
+ ...WebSocketInterceptorPreRN079,
572
+ setOnMessageCallback: (callback) => {
573
+ WebSocketInterceptorPreRN079.setOnMessageCallback((socketId, data) => {
574
+ callback(data, socketId);
575
+ });
576
+ },
577
+ setOnCloseCallback: (callback) => {
578
+ WebSocketInterceptorPreRN079.setOnCloseCallback((error, socketId) => {
579
+ callback(socketId, error);
580
+ });
581
+ },
582
+ setOnErrorCallback: (callback) => {
583
+ WebSocketInterceptorPreRN079.setOnErrorCallback((error, socketId) => {
584
+ callback(socketId, error);
585
+ });
586
+ }
587
+ };
588
+ }
589
+ };
590
+ const getWebSocketInspector = () => {
591
+ const eventEmitter = nanoevents.createNanoEvents();
592
+ const socketUrlMap = /* @__PURE__ */ new Map();
593
+ const webSocketInterceptor = getWebSocketInterceptor();
594
+ return {
595
+ enable: () => {
596
+ webSocketInterceptor.setConnectCallback(
597
+ (url, protocols, options, socketId) => {
598
+ socketUrlMap.set(socketId, url);
599
+ const event = {
600
+ type: "websocket-connect",
601
+ url,
602
+ socketId,
603
+ timestamp: Date.now(),
604
+ protocols,
605
+ options
606
+ };
607
+ eventEmitter.emit("websocket-connect", event);
608
+ }
609
+ );
610
+ webSocketInterceptor.setCloseCallback(
611
+ (code, reason, socketId) => {
612
+ const url = socketUrlMap.get(socketId);
613
+ if (!url) {
614
+ return;
615
+ }
616
+ const event = {
617
+ type: "websocket-close",
618
+ url,
619
+ socketId,
620
+ timestamp: Date.now(),
621
+ code: code || 0,
622
+ reason: reason || void 0
623
+ };
624
+ eventEmitter.emit("websocket-close", event);
625
+ socketUrlMap.delete(socketId);
626
+ }
627
+ );
628
+ webSocketInterceptor.setOnMessageCallback(
629
+ (data, socketId) => {
630
+ const url = socketUrlMap.get(socketId);
631
+ if (!url) {
632
+ return;
633
+ }
634
+ const event = {
635
+ type: "websocket-message-received",
636
+ url,
637
+ socketId,
638
+ timestamp: Date.now(),
639
+ data,
640
+ messageType: typeof data === "string" ? "text" : "binary"
641
+ };
642
+ eventEmitter.emit("websocket-message-received", event);
643
+ }
644
+ );
645
+ webSocketInterceptor.setOnErrorCallback(
646
+ (error, socketId) => {
647
+ const url = socketUrlMap.get(socketId);
648
+ if (!url) {
649
+ return;
650
+ }
651
+ const event = {
652
+ type: "websocket-error",
653
+ url,
654
+ socketId,
655
+ timestamp: Date.now(),
656
+ error
657
+ };
658
+ eventEmitter.emit("websocket-error", event);
659
+ }
660
+ );
661
+ webSocketInterceptor.setSendCallback((data, socketId) => {
662
+ const url = socketUrlMap.get(socketId);
663
+ if (!url) {
664
+ return;
665
+ }
666
+ const event = {
667
+ type: "websocket-message-sent",
668
+ url,
669
+ socketId,
670
+ timestamp: Date.now(),
671
+ data,
672
+ messageType: typeof data === "string" ? "text" : "binary"
673
+ };
674
+ eventEmitter.emit("websocket-message-sent", event);
675
+ });
676
+ webSocketInterceptor.setOnOpenCallback((socketId) => {
677
+ const url = socketUrlMap.get(socketId);
678
+ if (!url) {
679
+ return;
680
+ }
681
+ const event = {
682
+ type: "websocket-open",
683
+ url,
684
+ socketId,
685
+ timestamp: Date.now()
686
+ };
687
+ eventEmitter.emit("websocket-open", event);
688
+ });
689
+ webSocketInterceptor.setOnCloseCallback(
690
+ (error, socketId) => {
691
+ const url = socketUrlMap.get(socketId);
692
+ if (!url) {
693
+ return;
694
+ }
695
+ const event = {
696
+ type: "websocket-close",
697
+ url,
698
+ socketId,
699
+ timestamp: Date.now(),
700
+ code: error.code,
701
+ reason: error.reason
702
+ };
703
+ eventEmitter.emit("websocket-close", event);
704
+ socketUrlMap.delete(socketId);
705
+ }
706
+ );
707
+ webSocketInterceptor.enableInterception();
708
+ },
709
+ disable: () => {
710
+ webSocketInterceptor.disableInterception();
711
+ },
712
+ isEnabled: () => webSocketInterceptor.isInterceptorEnabled(),
713
+ dispose: () => {
714
+ eventEmitter.events = {};
715
+ socketUrlMap.clear();
716
+ },
717
+ on: (event, callback) => eventEmitter.on(event, callback)
718
+ };
719
+ };
720
+ let connectCallback;
721
+ let messageCallback;
722
+ let errorCallback;
723
+ let openEventCallback;
724
+ let closeCallback;
725
+ let isInterceptorEnabled = false;
726
+ const eventSourceClass = eventSource.getEventSource();
727
+ const originalOpen = eventSourceClass.prototype.open;
728
+ const originalDispatch = eventSourceClass.prototype.dispatch;
729
+ const BUILT_IN_EVENT_TYPES = /* @__PURE__ */ new Set(["open", "error", "close", "done"]);
730
+ const SSEInterceptor = {
731
+ /**
732
+ * Invoked when EventSource.open() is called (connection attempt starting).
733
+ */
734
+ setConnectCallback(callback) {
735
+ connectCallback = callback;
736
+ },
737
+ /**
738
+ * Invoked when a message event is received.
739
+ */
740
+ setMessageCallback(callback) {
741
+ messageCallback = callback;
742
+ },
743
+ /**
744
+ * Invoked when an error event occurs.
745
+ */
746
+ setErrorCallback(callback) {
747
+ errorCallback = callback;
748
+ },
749
+ /**
750
+ * Invoked when the connection is successfully opened (open event fired).
751
+ */
752
+ setOpenEventCallback(callback) {
753
+ openEventCallback = callback;
754
+ },
755
+ /**
756
+ * Invoked when the connection is closed.
757
+ */
758
+ setCloseCallback(callback) {
759
+ closeCallback = callback;
760
+ },
761
+ isInterceptorEnabled() {
762
+ return isInterceptorEnabled;
763
+ },
764
+ enableInterception() {
765
+ if (isInterceptorEnabled) {
766
+ return;
767
+ }
768
+ eventSourceClass.prototype.open = function() {
769
+ if (connectCallback) {
770
+ connectCallback(this.url, this);
771
+ }
772
+ this.addEventListener("open", (event) => {
773
+ if (openEventCallback) {
774
+ openEventCallback(event, this);
775
+ }
776
+ });
777
+ this.addEventListener(
778
+ "error",
779
+ (event) => {
780
+ if (errorCallback) {
781
+ errorCallback(event, this);
782
+ }
783
+ }
784
+ );
785
+ this.addEventListener("close", (event) => {
786
+ if (closeCallback) {
787
+ closeCallback(event, this);
788
+ }
789
+ });
790
+ return originalOpen.call(this);
791
+ };
792
+ eventSourceClass.prototype.dispatch = function(eventType, data) {
793
+ if (!BUILT_IN_EVENT_TYPES.has(eventType)) {
794
+ if (messageCallback) {
795
+ messageCallback(data, this);
796
+ }
797
+ }
798
+ return originalDispatch.call(this, eventType, data);
799
+ };
800
+ isInterceptorEnabled = true;
801
+ },
802
+ // Unpatch EventSource open method and remove the callbacks.
803
+ disableInterception() {
804
+ if (!isInterceptorEnabled) {
805
+ return;
806
+ }
807
+ isInterceptorEnabled = false;
808
+ eventSourceClass.prototype.open = originalOpen;
809
+ eventSourceClass.prototype.dispatch = originalDispatch;
810
+ connectCallback = null;
811
+ messageCallback = null;
812
+ errorCallback = null;
813
+ openEventCallback = null;
814
+ closeCallback = null;
815
+ }
816
+ };
817
+ const getSSEInspector = () => {
818
+ const eventEmitter = nanoevents.createNanoEvents();
819
+ const getRequestId = (eventSource2) => {
820
+ var _a;
821
+ const requestId = (_a = eventSource2._xhr) == null ? void 0 : _a._rozeniteRequestId;
822
+ if (!requestId) {
823
+ return null;
824
+ }
825
+ return requestId;
826
+ };
827
+ return {
828
+ enable: () => {
829
+ SSEInterceptor.setOpenEventCallback((_, eventSource2) => {
830
+ const sseEventSource = eventSource2;
831
+ const requestId = getRequestId(sseEventSource);
832
+ if (!requestId) {
833
+ return;
834
+ }
835
+ const sseXhr = sseEventSource._xhr;
836
+ const event = {
837
+ type: "sse-open",
838
+ requestId,
839
+ timestamp: Date.now(),
840
+ response: {
841
+ url: sseXhr._url,
842
+ status: sseXhr.status,
843
+ statusText: sseXhr.statusText,
844
+ headers: sseXhr.responseHeaders || {},
845
+ contentType: getContentType(sseXhr),
846
+ size: 0,
847
+ responseTime: Date.now()
848
+ }
849
+ };
850
+ eventEmitter.emit("sse-open", event);
851
+ });
852
+ SSEInterceptor.setMessageCallback((messageEvent, eventSource2) => {
853
+ const sseEventSource = eventSource2;
854
+ const requestId = getRequestId(sseEventSource);
855
+ if (!requestId) {
856
+ return;
857
+ }
858
+ const event = {
859
+ type: "sse-message",
860
+ requestId,
861
+ timestamp: Date.now(),
862
+ payload: {
863
+ type: messageEvent.type,
864
+ data: messageEvent.data || ""
865
+ }
866
+ };
867
+ eventEmitter.emit("sse-message", event);
868
+ });
869
+ SSEInterceptor.setErrorCallback((errorEvent, eventSource2) => {
870
+ const sseEventSource = eventSource2;
871
+ const requestId = getRequestId(sseEventSource);
872
+ if (!requestId) {
873
+ return;
874
+ }
875
+ const event = {
876
+ type: "sse-error",
877
+ requestId,
878
+ timestamp: Date.now(),
879
+ error: {
880
+ type: errorEvent.type,
881
+ message: errorEvent.type === "timeout" ? "Timeout" : errorEvent.message
882
+ }
883
+ };
884
+ eventEmitter.emit("sse-error", event);
885
+ });
886
+ SSEInterceptor.setCloseCallback((_, eventSource2) => {
887
+ const sseEventSource = eventSource2;
888
+ const requestId = getRequestId(sseEventSource);
889
+ if (!requestId) {
890
+ return;
891
+ }
892
+ const event = {
893
+ type: "sse-close",
894
+ requestId,
895
+ timestamp: Date.now()
896
+ };
897
+ eventEmitter.emit("sse-close", event);
898
+ });
899
+ SSEInterceptor.enableInterception();
900
+ },
901
+ disable: () => {
902
+ SSEInterceptor.disableInterception();
903
+ },
904
+ isEnabled: () => SSEInterceptor.isInterceptorEnabled(),
905
+ dispose: () => {
906
+ SSEInterceptor.disableInterception();
907
+ eventEmitter.events = {};
908
+ },
909
+ on: (event, callback) => eventEmitter.on(event, callback)
910
+ };
911
+ };
912
+ const DEFAULT_CONFIG = {
913
+ inspectors: {
914
+ http: true,
915
+ websocket: true,
916
+ sse: true
917
+ },
918
+ clientUISettings: {
919
+ showUrlAsName: false
920
+ }
921
+ };
922
+ const validateConfig = (config) => {
923
+ const inspectors = config.inspectors;
924
+ if (!inspectors) {
925
+ return;
926
+ }
927
+ if (inspectors.sse && !inspectors.http) {
928
+ throw new Error("SSE inspector requires HTTP inspector to be enabled.");
929
+ }
930
+ };
931
+ const overridesRegistry = getOverridesRegistry();
932
+ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
933
+ var _a, _b, _c, _d;
934
+ const isRecordingEnabledRef = react.useRef(false);
356
935
  const client = pluginBridge.useRozeniteDevToolsClient({
357
936
  pluginId: "@rozenite/network-activity-plugin"
358
937
  });
938
+ const isHttpInspectorEnabled = ((_a = config.inspectors) == null ? void 0 : _a.http) ?? true;
939
+ const isWebSocketInspectorEnabled = ((_b = config.inspectors) == null ? void 0 : _b.websocket) ?? true;
940
+ const isSSEInspectorEnabled = ((_c = config.inspectors) == null ? void 0 : _c.sse) ?? true;
941
+ const showUrlAsName = (_d = config.clientUISettings) == null ? void 0 : _d.showUrlAsName;
359
942
  react.useEffect(() => {
360
943
  if (!client) {
361
944
  return;
362
945
  }
946
+ validateConfig(config);
947
+ }, [config]);
948
+ react.useEffect(() => {
949
+ if (!client) {
950
+ return;
951
+ }
952
+ const sendClientUISettings = () => {
953
+ var _a2;
954
+ client.send("client-ui-settings", {
955
+ settings: {
956
+ showUrlAsName: showUrlAsName ?? ((_a2 = DEFAULT_CONFIG.clientUISettings) == null ? void 0 : _a2.showUrlAsName)
957
+ }
958
+ });
959
+ };
960
+ const subscriptions = [
961
+ client.onMessage("network-enable", () => {
962
+ isRecordingEnabledRef.current = true;
963
+ }),
964
+ client.onMessage("network-disable", () => {
965
+ isRecordingEnabledRef.current = false;
966
+ }),
967
+ client.onMessage("set-overrides", (data) => {
968
+ overridesRegistry.setOverrides(data.overrides);
969
+ }),
970
+ client.onMessage("get-client-ui-settings", () => {
971
+ sendClientUISettings();
972
+ })
973
+ ];
974
+ sendClientUISettings();
975
+ return () => {
976
+ subscriptions.forEach((subscription) => subscription.remove());
977
+ };
978
+ }, [client, showUrlAsName]);
979
+ react.useEffect(() => {
980
+ if (!client || !isHttpInspectorEnabled) {
981
+ return;
982
+ }
363
983
  const networkInspector = getNetworkInspector(client);
984
+ if (isRecordingEnabledRef.current) {
985
+ networkInspector.enable();
986
+ }
364
987
  return () => {
365
988
  networkInspector.dispose();
366
989
  };
367
- }, [client]);
990
+ }, [client, isHttpInspectorEnabled]);
991
+ react.useEffect(() => {
992
+ if (!client || !isWebSocketInspectorEnabled) {
993
+ return;
994
+ }
995
+ const eventsToForward = [
996
+ "websocket-connect",
997
+ "websocket-open",
998
+ "websocket-close",
999
+ "websocket-message-sent",
1000
+ "websocket-message-received",
1001
+ "websocket-error",
1002
+ "websocket-connection-status-changed"
1003
+ ];
1004
+ const websocketInspector = getWebSocketInspector();
1005
+ eventsToForward.forEach((event) => {
1006
+ websocketInspector.on(event, (event2) => {
1007
+ client.send(event2.type, event2);
1008
+ });
1009
+ });
1010
+ client.onMessage("network-enable", () => {
1011
+ websocketInspector.enable();
1012
+ });
1013
+ client.onMessage("network-disable", () => {
1014
+ websocketInspector.disable();
1015
+ });
1016
+ if (isRecordingEnabledRef.current) {
1017
+ websocketInspector.enable();
1018
+ }
1019
+ return () => {
1020
+ websocketInspector.dispose();
1021
+ };
1022
+ }, [client, isWebSocketInspectorEnabled]);
1023
+ react.useEffect(() => {
1024
+ if (!client || !isSSEInspectorEnabled) {
1025
+ return;
1026
+ }
1027
+ const eventsToForward = [
1028
+ "sse-open",
1029
+ "sse-message",
1030
+ "sse-error",
1031
+ "sse-close"
1032
+ ];
1033
+ const sseInspector = getSSEInspector();
1034
+ eventsToForward.forEach((event) => {
1035
+ sseInspector.on(event, (event2) => {
1036
+ client.send(event2.type, event2);
1037
+ });
1038
+ });
1039
+ client.onMessage("network-enable", () => {
1040
+ sseInspector.enable();
1041
+ });
1042
+ client.onMessage("network-disable", () => {
1043
+ sseInspector.disable();
1044
+ });
1045
+ if (isRecordingEnabledRef.current) {
1046
+ sseInspector.enable();
1047
+ }
1048
+ return () => {
1049
+ sseInspector.dispose();
1050
+ };
1051
+ }, [client, isSSEInspectorEnabled]);
368
1052
  return client;
369
1053
  };
370
1054
  exports.useNetworkActivityDevTools = useNetworkActivityDevTools;