@rozenite/network-activity-plugin 1.0.0-alpha.12 → 1.0.0-alpha.13

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 (42) hide show
  1. package/dist/App.html +2 -2
  2. package/dist/assets/{App-JuOeT_VQ.js → App-CZPlDq_M.js} +9056 -8965
  3. package/dist/assets/{App-DCuHdq4D.css → App-CfJuBHc_.css} +3 -0
  4. package/dist/rozenite.json +1 -1
  5. package/dist/src/shared/client.d.ts +9 -6
  6. package/dist/src/ui/components/CopyRequestDropdown.d.ts +7 -0
  7. package/dist/src/ui/components/RequestBody.d.ts +6 -0
  8. package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +2 -0
  9. package/dist/src/ui/utils/generateCurlCommand.d.ts +2 -7
  10. package/dist/src/ui/utils/generateFetchCall.d.ts +2 -0
  11. package/dist/src/ui/utils/generateMultipartBody.d.ts +4 -0
  12. package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +1 -2
  13. package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +1 -2
  14. package/dist/src/utils/getHttpHeaderValueAsString.d.ts +11 -0
  15. package/dist/src/utils/typeChecks.d.ts +9 -0
  16. package/dist/useNetworkActivityDevTools.cjs +61 -31
  17. package/dist/useNetworkActivityDevTools.js +61 -31
  18. package/package.json +4 -4
  19. package/src/react-native/http/network-inspector.ts +60 -28
  20. package/src/react-native/sse/sse-inspector.ts +22 -4
  21. package/src/shared/client.ts +22 -6
  22. package/src/ui/components/CodeBlock.tsx +1 -1
  23. package/src/ui/components/CopyRequestDropdown.tsx +95 -0
  24. package/src/ui/components/KeyValueGrid.tsx +1 -4
  25. package/src/ui/components/RequestBody.tsx +86 -0
  26. package/src/ui/components/RequestList.tsx +1 -1
  27. package/src/ui/state/model.ts +6 -1
  28. package/src/ui/tabs/CookiesTab.tsx +2 -6
  29. package/src/ui/tabs/HeadersTab.tsx +2 -7
  30. package/src/ui/tabs/RequestTab.tsx +35 -51
  31. package/src/ui/utils/checkRequestBodyBinary.ts +7 -0
  32. package/src/ui/utils/generateCurlCommand.ts +9 -12
  33. package/src/ui/utils/generateFetchCall.ts +64 -0
  34. package/src/ui/utils/generateMultipartBody.ts +19 -0
  35. package/src/utils/applyReactNativeRequestHeadersLogic.ts +2 -3
  36. package/src/utils/applyReactNativeResponseHeadersLogic.ts +2 -3
  37. package/src/utils/getHttpHeaderValueAsString.ts +13 -0
  38. package/src/utils/typeChecks.ts +27 -0
  39. package/dist/src/ui/components/CopyAsCurlButton.d.ts +0 -5
  40. package/dist/src/utils/isNumber.d.ts +0 -1
  41. package/src/ui/components/CopyAsCurlButton.tsx +0 -45
  42. package/src/utils/isNumber.ts +0 -3
@@ -845,6 +845,9 @@ video {
845
845
  .whitespace-nowrap {
846
846
  white-space: nowrap;
847
847
  }
848
+ .whitespace-pre {
849
+ white-space: pre;
850
+ }
848
851
  .whitespace-pre-wrap {
849
852
  white-space: pre-wrap;
850
853
  }
@@ -1 +1 @@
1
- {"name":"@rozenite/network-activity-plugin","version":"1.0.0-alpha.11","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/App.html"}]}
1
+ {"name":"@rozenite/network-activity-plugin","version":"1.0.0-alpha.12","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/App.html"}]}
@@ -7,20 +7,23 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD';
7
7
  export type RequestId = string;
8
8
  export type Timestamp = number;
9
9
  export type XHRPostData = string | Blob | FormData | ArrayBuffer | ArrayBufferView | unknown | null | undefined;
10
- export type RequestPostData = {
10
+ export type RequestTextPostData = {
11
11
  type: 'text';
12
12
  value: string;
13
- } | {
14
- type: 'form-data';
15
- value: Record<string, unknown>;
16
- } | {
13
+ };
14
+ export type RequestBinaryPostData = {
17
15
  type: 'binary';
18
16
  value: {
19
17
  size: number;
20
18
  type?: string;
21
19
  name?: string;
22
20
  };
23
- } | null | undefined;
21
+ };
22
+ export type RequestFormDataPostData = {
23
+ type: 'form-data';
24
+ value: Record<string, RequestTextPostData | RequestBinaryPostData>;
25
+ };
26
+ export type RequestPostData = RequestTextPostData | RequestFormDataPostData | RequestBinaryPostData | null | undefined;
24
27
  export type Cookie = {
25
28
  name: string;
26
29
  value: string;
@@ -0,0 +1,7 @@
1
+ import { HttpNetworkEntry, SSENetworkEntry } from '../state/model';
2
+ type NetworkEntry = HttpNetworkEntry | SSENetworkEntry;
3
+ type CopyDropdownProps = {
4
+ selectedRequest: NetworkEntry;
5
+ };
6
+ export declare const CopyRequestDropdown: ({ selectedRequest }: CopyDropdownProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export {};
@@ -0,0 +1,6 @@
1
+ import { HttpRequestData } from '../state/model';
2
+ type RequestBodyProps = {
3
+ data: HttpRequestData['data'];
4
+ };
5
+ export declare const RequestBody: ({ data }: RequestBodyProps) => import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
@@ -0,0 +1,2 @@
1
+ import { NetworkEntry } from '../state/model';
2
+ export declare const checkRequestBodyBinary: (request: NetworkEntry) => boolean;
@@ -1,7 +1,2 @@
1
- import { RequestPostData } from '../../shared/client';
2
- export declare function generateCurlCommand(request: {
3
- method: string;
4
- url: string;
5
- headers?: Record<string, string>;
6
- postData?: RequestPostData;
7
- }): string;
1
+ import { HttpNetworkEntry, SSENetworkEntry } from '../state/model';
2
+ export declare function generateCurlCommand(request: HttpNetworkEntry | SSENetworkEntry): string;
@@ -0,0 +1,2 @@
1
+ import { HttpNetworkEntry, SSENetworkEntry } from '../state/model';
2
+ export declare const generateFetchCall: (request: HttpNetworkEntry | SSENetworkEntry) => string;
@@ -0,0 +1,4 @@
1
+ export declare const generateMultipartBody: (formData: Record<string, unknown>) => {
2
+ body: string;
3
+ contentType: string;
4
+ };
@@ -2,7 +2,6 @@ import { HttpHeaders, RequestPostData } from '../shared/client';
2
2
  /**
3
3
  * Partially emulates React Native's behavior for setting HTTP headers.
4
4
  *
5
- * See:
6
- * https://github.com/facebook/react-native/blob/de5093c88771977b58f7bec3f3ffa64a9595334e/packages/react-native/Libraries/Network/RCTNetworking.mm#L345-L349
5
+ * @see https://github.com/facebook/react-native/blob/de5093c88771977b58f7bec3f3ffa64a9595334e/packages/react-native/Libraries/Network/RCTNetworking.mm#L345-L349
7
6
  */
8
7
  export declare function applyReactNativeRequestHeadersLogic(headers: HttpHeaders, postData?: RequestPostData): HttpHeaders;
@@ -4,7 +4,6 @@ import { HttpHeaders, XHRHeaders } from '../shared/client';
4
4
  * React Native concatenates multiple header values into single strings,
5
5
  * this function parses them back into arrays where appropriate.
6
6
  *
7
- * See:
8
- * https://github.com/facebook/react-native/blob/588f0c5ce6c283f116228456da2170d2adc3cbf4/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java#L637
7
+ * @see https://github.com/facebook/react-native/blob/588f0c5ce6c283f116228456da2170d2adc3cbf4/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java#L637
9
8
  */
10
9
  export declare const applyReactNativeResponseHeadersLogic: (headers: XHRHeaders) => HttpHeaders;
@@ -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,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[];
@@ -255,40 +255,60 @@ const applyReactNativeResponseHeadersLogic = (headers) => {
255
255
  }
256
256
  return parsedHeaders;
257
257
  };
258
+ const isBlob = (value) => value instanceof Blob;
259
+ const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
260
+ const isFormData = (value) => value instanceof FormData;
261
+ const isNullOrUndefined = (value) => value === null || value === void 0;
258
262
  const networkRequestsRegistry = getNetworkRequestsRegistry();
259
- function getRequestBody(body) {
260
- if (body === null || body === void 0) {
261
- return body;
263
+ const getBinaryPostData = (body) => ({
264
+ type: "binary",
265
+ value: {
266
+ size: body.size,
267
+ type: body.type,
268
+ name: getBlobName(body)
262
269
  }
263
- if (body instanceof Blob) {
264
- return {
265
- type: "binary",
266
- value: {
267
- size: body.size,
268
- type: body.type,
269
- name: getBlobName(body)
270
- }
271
- };
270
+ });
271
+ const getArrayBufferPostData = (body) => ({
272
+ type: "binary",
273
+ value: {
274
+ size: body.byteLength
272
275
  }
273
- if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
274
- return {
275
- type: "binary",
276
- value: {
277
- size: body.byteLength
276
+ });
277
+ const getTextPostData = (body) => ({
278
+ type: "text",
279
+ value: safeStringify(body)
280
+ });
281
+ const getFormDataPostData = (body) => ({
282
+ type: "form-data",
283
+ value: getFormDataEntries(body).reduce(
284
+ (acc, [key, value]) => {
285
+ if (isBlob(value)) {
286
+ acc[key] = getBinaryPostData(value);
287
+ } else if (isArrayBuffer(value)) {
288
+ acc[key] = getArrayBufferPostData(value);
289
+ } else {
290
+ acc[key] = getTextPostData(value);
278
291
  }
279
- };
292
+ return acc;
293
+ },
294
+ {}
295
+ )
296
+ });
297
+ const getRequestBody = (body) => {
298
+ if (isNullOrUndefined(body)) {
299
+ return body;
280
300
  }
281
- if (body instanceof FormData) {
282
- return {
283
- type: "form-data",
284
- value: Object.fromEntries(getFormDataEntries(body))
285
- };
301
+ if (isBlob(body)) {
302
+ return getBinaryPostData(body);
286
303
  }
287
- return {
288
- type: "text",
289
- value: safeStringify(body)
290
- };
291
- }
304
+ if (isArrayBuffer(body)) {
305
+ return getArrayBufferPostData(body);
306
+ }
307
+ if (isFormData(body)) {
308
+ return getFormDataPostData(body);
309
+ }
310
+ return getTextPostData(body);
311
+ };
292
312
  const getResponseSize = (request) => {
293
313
  try {
294
314
  const { responseType, response } = request;
@@ -732,9 +752,7 @@ const getSSEInspector = () => {
732
752
  var _a;
733
753
  const requestId = (_a = eventSource2._xhr) == null ? void 0 : _a._rozeniteRequestId;
734
754
  if (!requestId) {
735
- throw new Error(
736
- "No request ID found for EventSource. This should never happen!"
737
- );
755
+ return null;
738
756
  }
739
757
  return requestId;
740
758
  };
@@ -743,6 +761,9 @@ const getSSEInspector = () => {
743
761
  SSEInterceptor.setOpenEventCallback((_, eventSource2) => {
744
762
  const sseEventSource = eventSource2;
745
763
  const requestId = getRequestId(sseEventSource);
764
+ if (!requestId) {
765
+ return;
766
+ }
746
767
  const sseXhr = sseEventSource._xhr;
747
768
  const event = {
748
769
  type: "sse-open",
@@ -763,6 +784,9 @@ const getSSEInspector = () => {
763
784
  SSEInterceptor.setMessageCallback((messageEvent, eventSource2) => {
764
785
  const sseEventSource = eventSource2;
765
786
  const requestId = getRequestId(sseEventSource);
787
+ if (!requestId) {
788
+ return;
789
+ }
766
790
  const event = {
767
791
  type: "sse-message",
768
792
  requestId,
@@ -777,6 +801,9 @@ const getSSEInspector = () => {
777
801
  SSEInterceptor.setErrorCallback((errorEvent, eventSource2) => {
778
802
  const sseEventSource = eventSource2;
779
803
  const requestId = getRequestId(sseEventSource);
804
+ if (!requestId) {
805
+ return;
806
+ }
780
807
  const event = {
781
808
  type: "sse-error",
782
809
  requestId,
@@ -791,6 +818,9 @@ const getSSEInspector = () => {
791
818
  SSEInterceptor.setCloseCallback((_, eventSource2) => {
792
819
  const sseEventSource = eventSource2;
793
820
  const requestId = getRequestId(sseEventSource);
821
+ if (!requestId) {
822
+ return;
823
+ }
794
824
  const event = {
795
825
  type: "sse-close",
796
826
  requestId,
@@ -251,40 +251,60 @@ const applyReactNativeResponseHeadersLogic = (headers) => {
251
251
  }
252
252
  return parsedHeaders;
253
253
  };
254
+ const isBlob = (value) => value instanceof Blob;
255
+ const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
256
+ const isFormData = (value) => value instanceof FormData;
257
+ const isNullOrUndefined = (value) => value === null || value === void 0;
254
258
  const networkRequestsRegistry = getNetworkRequestsRegistry();
255
- function getRequestBody(body) {
256
- if (body === null || body === void 0) {
257
- return body;
259
+ const getBinaryPostData = (body) => ({
260
+ type: "binary",
261
+ value: {
262
+ size: body.size,
263
+ type: body.type,
264
+ name: getBlobName(body)
258
265
  }
259
- if (body instanceof Blob) {
260
- return {
261
- type: "binary",
262
- value: {
263
- size: body.size,
264
- type: body.type,
265
- name: getBlobName(body)
266
- }
267
- };
266
+ });
267
+ const getArrayBufferPostData = (body) => ({
268
+ type: "binary",
269
+ value: {
270
+ size: body.byteLength
268
271
  }
269
- if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
270
- return {
271
- type: "binary",
272
- value: {
273
- size: body.byteLength
272
+ });
273
+ const getTextPostData = (body) => ({
274
+ type: "text",
275
+ value: safeStringify(body)
276
+ });
277
+ const getFormDataPostData = (body) => ({
278
+ type: "form-data",
279
+ value: getFormDataEntries(body).reduce(
280
+ (acc, [key, value]) => {
281
+ if (isBlob(value)) {
282
+ acc[key] = getBinaryPostData(value);
283
+ } else if (isArrayBuffer(value)) {
284
+ acc[key] = getArrayBufferPostData(value);
285
+ } else {
286
+ acc[key] = getTextPostData(value);
274
287
  }
275
- };
288
+ return acc;
289
+ },
290
+ {}
291
+ )
292
+ });
293
+ const getRequestBody = (body) => {
294
+ if (isNullOrUndefined(body)) {
295
+ return body;
276
296
  }
277
- if (body instanceof FormData) {
278
- return {
279
- type: "form-data",
280
- value: Object.fromEntries(getFormDataEntries(body))
281
- };
297
+ if (isBlob(body)) {
298
+ return getBinaryPostData(body);
282
299
  }
283
- return {
284
- type: "text",
285
- value: safeStringify(body)
286
- };
287
- }
300
+ if (isArrayBuffer(body)) {
301
+ return getArrayBufferPostData(body);
302
+ }
303
+ if (isFormData(body)) {
304
+ return getFormDataPostData(body);
305
+ }
306
+ return getTextPostData(body);
307
+ };
288
308
  const getResponseSize = (request) => {
289
309
  try {
290
310
  const { responseType, response } = request;
@@ -728,9 +748,7 @@ const getSSEInspector = () => {
728
748
  var _a;
729
749
  const requestId = (_a = eventSource._xhr) == null ? void 0 : _a._rozeniteRequestId;
730
750
  if (!requestId) {
731
- throw new Error(
732
- "No request ID found for EventSource. This should never happen!"
733
- );
751
+ return null;
734
752
  }
735
753
  return requestId;
736
754
  };
@@ -739,6 +757,9 @@ const getSSEInspector = () => {
739
757
  SSEInterceptor.setOpenEventCallback((_, eventSource) => {
740
758
  const sseEventSource = eventSource;
741
759
  const requestId = getRequestId(sseEventSource);
760
+ if (!requestId) {
761
+ return;
762
+ }
742
763
  const sseXhr = sseEventSource._xhr;
743
764
  const event = {
744
765
  type: "sse-open",
@@ -759,6 +780,9 @@ const getSSEInspector = () => {
759
780
  SSEInterceptor.setMessageCallback((messageEvent, eventSource) => {
760
781
  const sseEventSource = eventSource;
761
782
  const requestId = getRequestId(sseEventSource);
783
+ if (!requestId) {
784
+ return;
785
+ }
762
786
  const event = {
763
787
  type: "sse-message",
764
788
  requestId,
@@ -773,6 +797,9 @@ const getSSEInspector = () => {
773
797
  SSEInterceptor.setErrorCallback((errorEvent, eventSource) => {
774
798
  const sseEventSource = eventSource;
775
799
  const requestId = getRequestId(sseEventSource);
800
+ if (!requestId) {
801
+ return;
802
+ }
776
803
  const event = {
777
804
  type: "sse-error",
778
805
  requestId,
@@ -787,6 +814,9 @@ const getSSEInspector = () => {
787
814
  SSEInterceptor.setCloseCallback((_, eventSource) => {
788
815
  const sseEventSource = eventSource;
789
816
  const requestId = getRequestId(sseEventSource);
817
+ if (!requestId) {
818
+ return;
819
+ }
790
820
  const event = {
791
821
  type: "sse-close",
792
822
  requestId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozenite/network-activity-plugin",
3
- "version": "1.0.0-alpha.12",
3
+ "version": "1.0.0-alpha.13",
4
4
  "description": "Network Activity for Rozenite.",
5
5
  "type": "module",
6
6
  "main": "./dist/react-native.cjs",
@@ -8,7 +8,7 @@
8
8
  "types": "./dist/react-native.d.ts",
9
9
  "dependencies": {
10
10
  "nanoevents": "^9.1.0",
11
- "@rozenite/plugin-bridge": "1.0.0-alpha.12"
11
+ "@rozenite/plugin-bridge": "1.0.0-alpha.13"
12
12
  },
13
13
  "devDependencies": {
14
14
  "@floating-ui/react": "^0.26.0",
@@ -36,8 +36,8 @@
36
36
  "zustand": "^5.0.6",
37
37
  "@types/react": "~18.3.23",
38
38
  "@types/react-dom": "~18.3.1",
39
- "@rozenite/vite-plugin": "1.0.0-alpha.12",
40
- "rozenite": "1.0.0-alpha.12"
39
+ "@rozenite/vite-plugin": "1.0.0-alpha.13",
40
+ "rozenite": "1.0.0-alpha.13"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react-native-sse": "*"
@@ -3,6 +3,9 @@ import {
3
3
  HttpMethod,
4
4
  NetworkActivityDevToolsClient,
5
5
  RequestPostData,
6
+ RequestTextPostData,
7
+ RequestBinaryPostData,
8
+ RequestFormDataPostData,
6
9
  XHRPostData,
7
10
  } from '../../shared/client';
8
11
  import { getContentType } from '../utils';
@@ -12,46 +15,75 @@ import { getFormDataEntries } from '../utils/getFormDataEntries';
12
15
  import { XHRInterceptor } from './xhr-interceptor';
13
16
  import { getStringSizeInBytes } from '../../utils/getStringSizeInBytes';
14
17
  import { applyReactNativeResponseHeadersLogic } from '../../utils/applyReactNativeResponseHeadersLogic';
18
+ import {
19
+ isBlob,
20
+ isArrayBuffer,
21
+ isFormData,
22
+ isNullOrUndefined,
23
+ } from '../../utils/typeChecks';
15
24
 
16
25
  const networkRequestsRegistry = getNetworkRequestsRegistry();
17
26
 
18
- function getRequestBody(body: XHRPostData): RequestPostData {
19
- if (body === null || body === undefined) {
27
+ const getBinaryPostData = (body: Blob): RequestBinaryPostData => ({
28
+ type: 'binary',
29
+ value: {
30
+ size: body.size,
31
+ type: body.type,
32
+ name: getBlobName(body),
33
+ },
34
+ });
35
+
36
+ const getArrayBufferPostData = (
37
+ body: ArrayBuffer | ArrayBufferView
38
+ ): RequestBinaryPostData => ({
39
+ type: 'binary',
40
+ value: {
41
+ size: body.byteLength,
42
+ },
43
+ });
44
+
45
+ const getTextPostData = (body: unknown): RequestTextPostData => ({
46
+ type: 'text',
47
+ value: safeStringify(body),
48
+ });
49
+
50
+ const getFormDataPostData = (body: FormData): RequestFormDataPostData => ({
51
+ type: 'form-data',
52
+ value: getFormDataEntries(body).reduce<RequestFormDataPostData['value']>(
53
+ (acc, [key, value]) => {
54
+ if (isBlob(value)) {
55
+ acc[key] = getBinaryPostData(value);
56
+ } else if (isArrayBuffer(value)) {
57
+ acc[key] = getArrayBufferPostData(value);
58
+ } else {
59
+ acc[key] = getTextPostData(value);
60
+ }
61
+
62
+ return acc;
63
+ },
64
+ {}
65
+ ),
66
+ });
67
+
68
+ const getRequestBody = (body: XHRPostData): RequestPostData => {
69
+ if (isNullOrUndefined(body)) {
20
70
  return body;
21
71
  }
22
72
 
23
- if (body instanceof Blob) {
24
- return {
25
- type: 'binary',
26
- value: {
27
- size: body.size,
28
- type: body.type,
29
- name: getBlobName(body),
30
- },
31
- };
73
+ if (isBlob(body)) {
74
+ return getBinaryPostData(body);
32
75
  }
33
76
 
34
- if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
35
- return {
36
- type: 'binary',
37
- value: {
38
- size: body.byteLength,
39
- },
40
- };
77
+ if (isArrayBuffer(body)) {
78
+ return getArrayBufferPostData(body);
41
79
  }
42
80
 
43
- if (body instanceof FormData) {
44
- return {
45
- type: 'form-data',
46
- value: Object.fromEntries(getFormDataEntries(body)),
47
- };
81
+ if (isFormData(body)) {
82
+ return getFormDataPostData(body);
48
83
  }
49
84
 
50
- return {
51
- type: 'text',
52
- value: safeStringify(body),
53
- };
54
- }
85
+ return getTextPostData(body);
86
+ };
55
87
 
56
88
  const getResponseSize = (request: XMLHttpRequest): number | null => {
57
89
  try {
@@ -22,13 +22,14 @@ export type SSEInspector = {
22
22
  export const getSSEInspector = (): SSEInspector => {
23
23
  const eventEmitter = createNanoEvents<NanoEventsMap>();
24
24
 
25
- const getRequestId = (eventSource: EventSourceWithInternals): string => {
25
+ const getRequestId = (
26
+ eventSource: EventSourceWithInternals
27
+ ): string | null => {
26
28
  const requestId = eventSource._xhr?._rozeniteRequestId;
27
29
 
28
30
  if (!requestId) {
29
- throw new Error(
30
- 'No request ID found for EventSource. This should never happen!'
31
- );
31
+ // It means that the EventSource was created before the inspector was enabled.
32
+ return null;
32
33
  }
33
34
 
34
35
  return requestId;
@@ -39,6 +40,11 @@ export const getSSEInspector = (): SSEInspector => {
39
40
  SSEInterceptor.setOpenEventCallback((_, eventSource) => {
40
41
  const sseEventSource = eventSource as EventSourceWithInternals;
41
42
  const requestId = getRequestId(sseEventSource);
43
+
44
+ if (!requestId) {
45
+ return;
46
+ }
47
+
42
48
  const sseXhr = sseEventSource._xhr as XMLHttpRequest;
43
49
 
44
50
  const event: SSEEvent = {
@@ -62,6 +68,10 @@ export const getSSEInspector = (): SSEInspector => {
62
68
  const sseEventSource = eventSource as EventSourceWithInternals;
63
69
  const requestId = getRequestId(sseEventSource);
64
70
 
71
+ if (!requestId) {
72
+ return;
73
+ }
74
+
65
75
  const event: SSEEvent = {
66
76
  type: 'sse-message',
67
77
  requestId,
@@ -78,6 +88,10 @@ export const getSSEInspector = (): SSEInspector => {
78
88
  const sseEventSource = eventSource as EventSourceWithInternals;
79
89
  const requestId = getRequestId(sseEventSource);
80
90
 
91
+ if (!requestId) {
92
+ return;
93
+ }
94
+
81
95
  const event: SSEEvent = {
82
96
  type: 'sse-error',
83
97
  requestId,
@@ -95,6 +109,10 @@ export const getSSEInspector = (): SSEInspector => {
95
109
  const sseEventSource = eventSource as EventSourceWithInternals;
96
110
  const requestId = getRequestId(sseEventSource);
97
111
 
112
+ if (!requestId) {
113
+ return;
114
+ }
115
+
98
116
  const event: SSEEvent = {
99
117
  type: 'sse-close',
100
118
  requestId,
@@ -20,13 +20,29 @@ export type XHRPostData =
20
20
  | null
21
21
  | undefined;
22
22
 
23
+ export type RequestTextPostData = {
24
+ type: 'text';
25
+ value: string;
26
+ };
27
+
28
+ export type RequestBinaryPostData = {
29
+ type: 'binary';
30
+ value: {
31
+ size: number;
32
+ type?: string;
33
+ name?: string;
34
+ };
35
+ };
36
+
37
+ export type RequestFormDataPostData = {
38
+ type: 'form-data';
39
+ value: Record<string, RequestTextPostData | RequestBinaryPostData>;
40
+ };
41
+
23
42
  export type RequestPostData =
24
- | { type: 'text'; value: string }
25
- | { type: 'form-data'; value: Record<string, unknown> }
26
- | {
27
- type: 'binary';
28
- value: { size: number; type?: string; name?: string };
29
- }
43
+ | RequestTextPostData
44
+ | RequestFormDataPostData
45
+ | RequestBinaryPostData
30
46
  | null
31
47
  | undefined;
32
48
 
@@ -4,7 +4,7 @@ import { cn } from '../utils/cn';
4
4
  export type CodeBlockProps = HTMLProps<HTMLPreElement>;
5
5
 
6
6
  const codeBlockClassNames =
7
- 'text-sm font-mono text-gray-300 whitespace-pre-wrap bg-gray-800 p-3 rounded border border-gray-700 overflow-x-auto wrap-anywhere';
7
+ 'text-sm font-mono text-gray-300 whitespace-pre-wrap bg-gray-800 p-3 rounded-md border border-gray-700 overflow-x-auto wrap-anywhere';
8
8
 
9
9
  export const CodeBlock = ({
10
10
  children,