@rozenite/network-activity-plugin 1.0.0-alpha.15 → 1.0.0-alpha.16

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 (30) hide show
  1. package/dist/App.html +2 -2
  2. package/dist/assets/{App-CfJuBHc_.css → App-BrSkOkws.css} +21 -0
  3. package/dist/assets/{App-CZPlDq_M.js → App-CM3Ub2ZA.js} +911 -479
  4. package/dist/rozenite.json +1 -1
  5. package/dist/src/react-native/http/overrides-registry.d.ts +6 -0
  6. package/dist/src/react-native/http/xhr-interceptor.d.ts +5 -0
  7. package/dist/src/shared/client.d.ts +7 -0
  8. package/dist/src/ui/components/CodeEditor.d.ts +5 -0
  9. package/dist/src/ui/components/OverrideResponse.d.ts +8 -0
  10. package/dist/src/ui/components/RequestList.d.ts +3 -2
  11. package/dist/src/ui/components/Section.d.ts +2 -1
  12. package/dist/src/ui/state/hooks.d.ts +3 -0
  13. package/dist/src/ui/state/store.d.ts +26 -3
  14. package/dist/useNetworkActivityDevTools.cjs +72 -0
  15. package/dist/useNetworkActivityDevTools.js +72 -0
  16. package/package.json +4 -4
  17. package/src/react-native/http/network-inspector.ts +50 -0
  18. package/src/react-native/http/overrides-registry.ts +32 -0
  19. package/src/react-native/http/xhr-interceptor.ts +14 -0
  20. package/src/react-native/useNetworkActivityDevTools.ts +6 -0
  21. package/src/shared/client.ts +9 -0
  22. package/src/ui/components/CodeEditor.tsx +26 -0
  23. package/src/ui/components/OverrideResponse.tsx +132 -0
  24. package/src/ui/components/RequestList.tsx +15 -8
  25. package/src/ui/components/Section.tsx +5 -1
  26. package/src/ui/components/SidePanel.tsx +8 -0
  27. package/src/ui/state/hooks.ts +4 -0
  28. package/src/ui/state/store.ts +585 -502
  29. package/src/ui/tabs/ResponseTab.tsx +60 -12
  30. package/src/ui/views/InspectorView.tsx +7 -1
@@ -1 +1 @@
1
- {"name":"@rozenite/network-activity-plugin","version":"1.0.0-alpha.12","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/App.html"}]}
1
+ {"name":"@rozenite/network-activity-plugin","version":"1.0.0-alpha.15","description":"Network Activity for Rozenite.","panels":[{"name":"Network Activity","source":"/App.html"}]}
@@ -0,0 +1,6 @@
1
+ import { RequestOverride } from '../../shared/client';
2
+ export type OverridesRegistry = {
3
+ setOverrides: (newOverrides: [string, RequestOverride][]) => void;
4
+ getOverrideForUrl: (url: string) => RequestOverride | undefined;
5
+ };
6
+ export declare const getOverridesRegistry: () => OverridesRegistry;
@@ -4,6 +4,7 @@ type XHRInterceptorSendCallback = (data: XHRPostData, request: XMLHttpRequest) =
4
4
  type XHRInterceptorRequestHeaderCallback = (header: string, value: string, request: XMLHttpRequest) => void;
5
5
  type XHRInterceptorHeaderReceivedCallback = (responseContentType: string | undefined, responseSize: number | undefined, allHeaders: string, request: XMLHttpRequest) => void;
6
6
  type XHRInterceptorResponseCallback = (status: number, timeout: number, response: string, responseURL: string, responseType: string, request: XMLHttpRequest) => void;
7
+ type XHRInterceptorOverrideCallback = (request: XMLHttpRequest) => void;
7
8
  /**
8
9
  * A network interceptor which monkey-patches XMLHttpRequest methods
9
10
  * to gather all network requests/responses, in order to show their
@@ -32,6 +33,10 @@ export declare const XHRInterceptor: {
32
33
  * Invoked before XMLHttpRequest.setRequestHeader(...) is called.
33
34
  */
34
35
  setRequestHeaderCallback(callback: XHRInterceptorRequestHeaderCallback): void;
36
+ /**
37
+ * Invoked before XMLHttpRequest.send(...) is called.
38
+ */
39
+ setOverrideCallback(callback: XHRInterceptorOverrideCallback): void;
35
40
  isInterceptorEnabled(): boolean;
36
41
  enableInterception(): void;
37
42
  disableInterception(): void;
@@ -57,6 +57,10 @@ export type Initiator = {
57
57
  columnNumber?: number;
58
58
  };
59
59
  export type ResourceType = 'XHR' | 'Fetch' | 'Other';
60
+ export type RequestOverride = {
61
+ status?: number;
62
+ body?: string;
63
+ };
60
64
  export type NetworkActivityEventMap = {
61
65
  'network-enable': unknown;
62
66
  'network-disable': unknown;
@@ -94,5 +98,8 @@ export type NetworkActivityEventMap = {
94
98
  requestId: RequestId;
95
99
  body: string | null;
96
100
  };
101
+ 'set-overrides': {
102
+ overrides: [string, RequestOverride][];
103
+ };
97
104
  } & WebSocketEventMap & SSEEventMap;
98
105
  export type NetworkActivityDevToolsClient = RozeniteDevToolsClient<NetworkActivityEventMap>;
@@ -0,0 +1,5 @@
1
+ export type CodeEditorProps = {
2
+ data: string | undefined;
3
+ onInput?: (event: React.FormEvent<HTMLPreElement>) => void;
4
+ };
5
+ export declare const CodeEditor: import('react').ForwardRefExoticComponent<CodeEditorProps & import('react').RefAttributes<HTMLPreElement>>;
@@ -0,0 +1,8 @@
1
+ import { HttpNetworkEntry } from '../state/model';
2
+ import { RequestOverride } from '../../shared/client';
3
+ export type OverrideResponseProps = {
4
+ selectedRequest: HttpNetworkEntry;
5
+ initialOverride: RequestOverride | undefined;
6
+ onClear: () => void;
7
+ };
8
+ export declare const OverrideResponse: ({ selectedRequest, initialOverride, onClear, }: OverrideResponseProps) => import("react/jsx-runtime").JSX.Element | undefined;
@@ -1,5 +1,5 @@
1
1
  import { ProcessedRequest } from '../state/model';
2
- import { RequestId } from '../../shared/client';
2
+ import { RequestId, RequestOverride } from '../../shared/client';
3
3
  import { FilterState } from './FilterBar';
4
4
  type NetworkRequest = {
5
5
  id: RequestId;
@@ -12,6 +12,7 @@ type NetworkRequest = {
12
12
  time: string;
13
13
  type: string;
14
14
  startTime: string;
15
+ hasOverride: boolean;
15
16
  };
16
17
  declare const formatSize: (bytes: number) => string;
17
18
  declare const formatDuration: (duration: number) => string;
@@ -21,7 +22,7 @@ declare const extractDomainAndPath: (url: string) => {
21
22
  path: string;
22
23
  };
23
24
  declare const generateName: (url: string) => string;
24
- declare const processNetworkRequests: (processedRequests: ProcessedRequest[]) => NetworkRequest[];
25
+ declare const processNetworkRequests: (processedRequests: ProcessedRequest[], overrides: Map<string, RequestOverride>) => NetworkRequest[];
25
26
  export type RequestListProps = {
26
27
  filter: FilterState;
27
28
  };
@@ -3,5 +3,6 @@ export type SectionProps = {
3
3
  title: string;
4
4
  children: React.ReactNode;
5
5
  collapsible?: boolean;
6
+ action?: React.ReactNode;
6
7
  };
7
- export declare const Section: ({ title, children, collapsible, }: SectionProps) => import("react/jsx-runtime").JSX.Element;
8
+ export declare const Section: ({ title, children, collapsible, action, }: SectionProps) => import("react/jsx-runtime").JSX.Element;
@@ -9,9 +9,12 @@ export declare const useNetworkActivityActions: () => {
9
9
  setRecording: (isRecording: boolean) => void;
10
10
  setSelectedRequest: (requestId: import('../../shared/client').RequestId | null) => void;
11
11
  clearRequests: () => void;
12
+ addOverride: (requestUrl: string, override: import('../../shared/client').RequestOverride) => void;
13
+ clearOverride: (requestUrl: string) => void;
12
14
  };
13
15
  export declare const useNetworkActivityClientManagement: () => {
14
16
  setupClient: (client: import('../../shared/client').NetworkActivityDevToolsClient) => void;
15
17
  cleanupClient: () => void;
16
18
  };
17
19
  export declare const useWebSocketMessages: (requestId: string) => import('./model').WebSocketMessage[];
20
+ export declare const useOverrides: () => Map<string, import('../../shared/client').RequestOverride>;
@@ -1,10 +1,11 @@
1
- import { NetworkActivityDevToolsClient, NetworkActivityEventMap, RequestId } from '../../shared/client';
1
+ import { NetworkActivityDevToolsClient, NetworkActivityEventMap, RequestOverride, RequestId } from '../../shared/client';
2
2
  import { NetworkEntry, WebSocketMessage } from './model';
3
3
  export interface NetworkActivityState {
4
4
  isRecording: boolean;
5
5
  selectedRequestId: RequestId | null;
6
6
  networkEntries: Map<RequestId, NetworkEntry>;
7
7
  websocketMessages: Map<RequestId, WebSocketMessage[]>;
8
+ overrides: Map<string, RequestOverride>;
8
9
  _unsubscribeFunctions?: Array<{
9
10
  remove: () => void;
10
11
  }>;
@@ -13,6 +14,8 @@ export interface NetworkActivityState {
13
14
  setRecording: (isRecording: boolean) => void;
14
15
  setSelectedRequest: (requestId: RequestId | null) => void;
15
16
  clearRequests: () => void;
17
+ addOverride: (requestUrl: string, override: RequestOverride) => void;
18
+ clearOverride: (requestUrl: string) => void;
16
19
  };
17
20
  handleEvent: <K extends keyof NetworkActivityEventMap>(eventType: K, data: NetworkActivityEventMap[K]) => void;
18
21
  client: {
@@ -20,5 +23,25 @@ export interface NetworkActivityState {
20
23
  cleanupClient: () => void;
21
24
  };
22
25
  }
23
- export declare const createNetworkActivityStore: () => import('zustand').StoreApi<NetworkActivityState>;
24
- export declare const store: import('zustand').StoreApi<NetworkActivityState>;
26
+ export declare const createNetworkActivityStore: () => Omit<import('zustand').StoreApi<NetworkActivityState>, "persist"> & {
27
+ persist: {
28
+ setOptions: (options: Partial<import('zustand/middleware').PersistOptions<NetworkActivityState, unknown>>) => void;
29
+ clearStorage: () => void;
30
+ rehydrate: () => Promise<void> | void;
31
+ hasHydrated: () => boolean;
32
+ onHydrate: (fn: (state: NetworkActivityState) => void) => () => void;
33
+ onFinishHydration: (fn: (state: NetworkActivityState) => void) => () => void;
34
+ getOptions: () => Partial<import('zustand/middleware').PersistOptions<NetworkActivityState, unknown>>;
35
+ };
36
+ };
37
+ export declare const store: Omit<import('zustand').StoreApi<NetworkActivityState>, "persist"> & {
38
+ persist: {
39
+ setOptions: (options: Partial<import('zustand/middleware').PersistOptions<NetworkActivityState, unknown>>) => void;
40
+ clearStorage: () => void;
41
+ rehydrate: () => Promise<void> | void;
42
+ hasHydrated: () => boolean;
43
+ onHydrate: (fn: (state: NetworkActivityState) => void) => () => void;
44
+ onFinishHydration: (fn: (state: NetworkActivityState) => void) => () => void;
45
+ getOptions: () => Partial<import('zustand/middleware').PersistOptions<NetworkActivityState, unknown>>;
46
+ };
47
+ };
@@ -116,6 +116,7 @@ let sendCallback;
116
116
  let requestHeaderCallback;
117
117
  let headerReceivedCallback;
118
118
  let responseCallback;
119
+ let overrideCallback;
119
120
  let isInterceptorEnabled$1 = false;
120
121
  const XHRInterceptor = {
121
122
  /**
@@ -148,6 +149,12 @@ const XHRInterceptor = {
148
149
  setRequestHeaderCallback(callback) {
149
150
  requestHeaderCallback = callback;
150
151
  },
152
+ /**
153
+ * Invoked before XMLHttpRequest.send(...) is called.
154
+ */
155
+ setOverrideCallback(callback) {
156
+ overrideCallback = callback;
157
+ },
151
158
  isInterceptorEnabled() {
152
159
  return isInterceptorEnabled$1;
153
160
  },
@@ -171,6 +178,9 @@ const XHRInterceptor = {
171
178
  if (sendCallback) {
172
179
  sendCallback(data, this);
173
180
  }
181
+ if (overrideCallback) {
182
+ overrideCallback(this);
183
+ }
174
184
  if (this.addEventListener) {
175
185
  this.addEventListener(
176
186
  "readystatechange",
@@ -231,6 +241,7 @@ const XHRInterceptor = {
231
241
  sendCallback = null;
232
242
  headerReceivedCallback = null;
233
243
  requestHeaderCallback = null;
244
+ overrideCallback = null;
234
245
  }
235
246
  };
236
247
  const getStringSizeInBytes = (value) => {
@@ -259,7 +270,28 @@ const isBlob = (value) => value instanceof Blob;
259
270
  const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
260
271
  const isFormData = (value) => value instanceof FormData;
261
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
+ };
262
293
  const networkRequestsRegistry = getNetworkRequestsRegistry();
294
+ const overridesRegistry$1 = getOverridesRegistry();
263
295
  const getBinaryPostData = (body) => ({
264
296
  type: "binary",
265
297
  value: {
@@ -449,9 +481,45 @@ const getNetworkInspector = (pluginClient) => {
449
481
  });
450
482
  });
451
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
+ };
452
519
  const enable = () => {
453
520
  XHRInterceptor.disableInterception();
454
521
  XHRInterceptor.setSendCallback(handleRequestSend);
522
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
455
523
  XHRInterceptor.enableInterception();
456
524
  };
457
525
  const disable = () => {
@@ -857,6 +925,7 @@ const validateConfig = (config) => {
857
925
  throw new Error("SSE inspector requires HTTP inspector to be enabled.");
858
926
  }
859
927
  };
928
+ const overridesRegistry = getOverridesRegistry();
860
929
  const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
861
930
  var _a, _b, _c;
862
931
  const isRecordingEnabledRef = react.useRef(false);
@@ -882,6 +951,9 @@ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
882
951
  }),
883
952
  client.onMessage("network-disable", () => {
884
953
  isRecordingEnabledRef.current = false;
954
+ }),
955
+ client.onMessage("set-overrides", (data) => {
956
+ overridesRegistry.setOverrides(data.overrides);
885
957
  })
886
958
  ];
887
959
  return () => {
@@ -112,6 +112,7 @@ let sendCallback;
112
112
  let requestHeaderCallback;
113
113
  let headerReceivedCallback;
114
114
  let responseCallback;
115
+ let overrideCallback;
115
116
  let isInterceptorEnabled$1 = false;
116
117
  const XHRInterceptor = {
117
118
  /**
@@ -144,6 +145,12 @@ const XHRInterceptor = {
144
145
  setRequestHeaderCallback(callback) {
145
146
  requestHeaderCallback = callback;
146
147
  },
148
+ /**
149
+ * Invoked before XMLHttpRequest.send(...) is called.
150
+ */
151
+ setOverrideCallback(callback) {
152
+ overrideCallback = callback;
153
+ },
147
154
  isInterceptorEnabled() {
148
155
  return isInterceptorEnabled$1;
149
156
  },
@@ -167,6 +174,9 @@ const XHRInterceptor = {
167
174
  if (sendCallback) {
168
175
  sendCallback(data, this);
169
176
  }
177
+ if (overrideCallback) {
178
+ overrideCallback(this);
179
+ }
170
180
  if (this.addEventListener) {
171
181
  this.addEventListener(
172
182
  "readystatechange",
@@ -227,6 +237,7 @@ const XHRInterceptor = {
227
237
  sendCallback = null;
228
238
  headerReceivedCallback = null;
229
239
  requestHeaderCallback = null;
240
+ overrideCallback = null;
230
241
  }
231
242
  };
232
243
  const getStringSizeInBytes = (value) => {
@@ -255,7 +266,28 @@ const isBlob = (value) => value instanceof Blob;
255
266
  const isArrayBuffer = (value) => value instanceof ArrayBuffer || ArrayBuffer.isView(value);
256
267
  const isFormData = (value) => value instanceof FormData;
257
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
+ };
258
289
  const networkRequestsRegistry = getNetworkRequestsRegistry();
290
+ const overridesRegistry$1 = getOverridesRegistry();
259
291
  const getBinaryPostData = (body) => ({
260
292
  type: "binary",
261
293
  value: {
@@ -445,9 +477,45 @@ const getNetworkInspector = (pluginClient) => {
445
477
  });
446
478
  });
447
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
+ };
448
515
  const enable = () => {
449
516
  XHRInterceptor.disableInterception();
450
517
  XHRInterceptor.setSendCallback(handleRequestSend);
518
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
451
519
  XHRInterceptor.enableInterception();
452
520
  };
453
521
  const disable = () => {
@@ -853,6 +921,7 @@ const validateConfig = (config) => {
853
921
  throw new Error("SSE inspector requires HTTP inspector to be enabled.");
854
922
  }
855
923
  };
924
+ const overridesRegistry = getOverridesRegistry();
856
925
  const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
857
926
  var _a, _b, _c;
858
927
  const isRecordingEnabledRef = useRef(false);
@@ -878,6 +947,9 @@ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
878
947
  }),
879
948
  client.onMessage("network-disable", () => {
880
949
  isRecordingEnabledRef.current = false;
950
+ }),
951
+ client.onMessage("set-overrides", (data) => {
952
+ overridesRegistry.setOverrides(data.overrides);
881
953
  })
882
954
  ];
883
955
  return () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rozenite/network-activity-plugin",
3
- "version": "1.0.0-alpha.15",
3
+ "version": "1.0.0-alpha.16",
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.15"
11
+ "@rozenite/plugin-bridge": "1.0.0-alpha.16"
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.15",
40
- "rozenite": "1.0.0-alpha.15"
39
+ "@rozenite/vite-plugin": "1.0.0-alpha.16",
40
+ "rozenite": "1.0.0-alpha.16"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "react-native-sse": "*"
@@ -21,8 +21,10 @@ import {
21
21
  isFormData,
22
22
  isNullOrUndefined,
23
23
  } from '../../utils/typeChecks';
24
+ import { getOverridesRegistry } from './overrides-registry';
24
25
 
25
26
  const networkRequestsRegistry = getNetworkRequestsRegistry();
27
+ const overridesRegistry = getOverridesRegistry();
26
28
 
27
29
  const getBinaryPostData = (body: Blob): RequestBinaryPostData => ({
28
30
  type: 'binary',
@@ -281,9 +283,57 @@ export const getNetworkInspector = (
281
283
  });
282
284
  };
283
285
 
286
+ const handleRequestOverride = (request: XMLHttpRequest): void => {
287
+ const override = overridesRegistry.getOverrideForUrl(
288
+ request._url as string
289
+ );
290
+
291
+ if (!override) {
292
+ return;
293
+ }
294
+
295
+ request.addEventListener('readystatechange', () => {
296
+ if (override.body !== undefined) {
297
+ Object.defineProperty(request, 'responseType', {
298
+ writable: true,
299
+ });
300
+
301
+ Object.defineProperty(request, 'response', {
302
+ writable: true,
303
+ });
304
+ Object.defineProperty(request, 'responseText', {
305
+ writable: true,
306
+ });
307
+
308
+ const contentType = getContentType(request);
309
+
310
+ if (contentType === 'application/json') {
311
+ request.responseType = 'json';
312
+ } else if (contentType === 'text/plain') {
313
+ request.responseType = 'text';
314
+ }
315
+
316
+ // @ts-expect-error - Mocking response
317
+ request.response = override.body;
318
+ // @ts-expect-error - Mocking responseText
319
+ request.responseText = override.body;
320
+ }
321
+
322
+ if (override.status !== undefined) {
323
+ Object.defineProperty(request, 'status', {
324
+ writable: true,
325
+ });
326
+
327
+ // @ts-expect-error - Mocking status
328
+ request.status = override.status;
329
+ }
330
+ });
331
+ };
332
+
284
333
  const enable = () => {
285
334
  XHRInterceptor.disableInterception();
286
335
  XHRInterceptor.setSendCallback(handleRequestSend);
336
+ XHRInterceptor.setOverrideCallback(handleRequestOverride);
287
337
  XHRInterceptor.enableInterception();
288
338
  };
289
339
 
@@ -0,0 +1,32 @@
1
+ import { RequestOverride } from '../../shared/client';
2
+
3
+ export type OverridesRegistry = {
4
+ setOverrides: (newOverrides: [string, RequestOverride][]) => void;
5
+ getOverrideForUrl: (url: string) => RequestOverride | undefined;
6
+ };
7
+
8
+ const createOverridesRegistry = (): OverridesRegistry => {
9
+ let overrides = new Map<string, RequestOverride>();
10
+
11
+ const setOverrides = (newOverrides: [string, RequestOverride][]) => {
12
+ overrides = new Map(newOverrides);
13
+ };
14
+
15
+ const getOverrideForUrl = (url: string) => {
16
+ return overrides.get(url);
17
+ };
18
+
19
+ return {
20
+ setOverrides,
21
+ getOverrideForUrl,
22
+ };
23
+ };
24
+
25
+ let registryInstance: OverridesRegistry | null = null;
26
+
27
+ export const getOverridesRegistry = (): OverridesRegistry => {
28
+ if (!registryInstance) {
29
+ registryInstance = createOverridesRegistry();
30
+ }
31
+ return registryInstance;
32
+ };
@@ -49,11 +49,14 @@ type XHRInterceptorResponseCallback = (
49
49
  request: XMLHttpRequest
50
50
  ) => void;
51
51
 
52
+ type XHRInterceptorOverrideCallback = (request: XMLHttpRequest) => void;
53
+
52
54
  let openCallback: XHRInterceptorOpenCallback | null;
53
55
  let sendCallback: XHRInterceptorSendCallback | null;
54
56
  let requestHeaderCallback: XHRInterceptorRequestHeaderCallback | null;
55
57
  let headerReceivedCallback: XHRInterceptorHeaderReceivedCallback | null;
56
58
  let responseCallback: XHRInterceptorResponseCallback | null;
59
+ let overrideCallback: XHRInterceptorOverrideCallback | null;
57
60
 
58
61
  let isInterceptorEnabled = false;
59
62
 
@@ -100,6 +103,13 @@ export const XHRInterceptor = {
100
103
  requestHeaderCallback = callback;
101
104
  },
102
105
 
106
+ /**
107
+ * Invoked before XMLHttpRequest.send(...) is called.
108
+ */
109
+ setOverrideCallback(callback: XHRInterceptorOverrideCallback) {
110
+ overrideCallback = callback;
111
+ },
112
+
103
113
  isInterceptorEnabled(): boolean {
104
114
  return isInterceptorEnabled;
105
115
  },
@@ -143,6 +153,9 @@ export const XHRInterceptor = {
143
153
  if (sendCallback) {
144
154
  sendCallback(data, this);
145
155
  }
156
+ if (overrideCallback) {
157
+ overrideCallback(this);
158
+ }
146
159
  if (this.addEventListener) {
147
160
  this.addEventListener(
148
161
  'readystatechange',
@@ -210,5 +223,6 @@ export const XHRInterceptor = {
210
223
  sendCallback = null;
211
224
  headerReceivedCallback = null;
212
225
  requestHeaderCallback = null;
226
+ overrideCallback = null;
213
227
  },
214
228
  };
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useRef } from 'react';
2
2
  import { useRozeniteDevToolsClient } from '@rozenite/plugin-bridge';
3
3
  import { getNetworkInspector } from './http/network-inspector';
4
+ import { getOverridesRegistry } from './http/overrides-registry';
4
5
  import { NetworkActivityEventMap } from '../shared/client';
5
6
  import { getWebSocketInspector } from './websocket/websocket-inspector';
6
7
  import { WebSocketEventMap } from '../shared/websocket-events';
@@ -13,6 +14,8 @@ import {
13
14
  validateConfig,
14
15
  } from './config';
15
16
 
17
+ const overridesRegistry = getOverridesRegistry();
18
+
16
19
  export const useNetworkActivityDevTools = (
17
20
  config: NetworkActivityDevToolsConfig = DEFAULT_CONFIG
18
21
  ) => {
@@ -46,6 +49,9 @@ export const useNetworkActivityDevTools = (
46
49
  client.onMessage('network-disable', () => {
47
50
  isRecordingEnabledRef.current = false;
48
51
  }),
52
+ client.onMessage('set-overrides', (data) => {
53
+ overridesRegistry.setOverrides(data.overrides);
54
+ }),
49
55
  ];
50
56
 
51
57
  return () => {
@@ -84,6 +84,11 @@ export type Initiator = {
84
84
 
85
85
  export type ResourceType = 'XHR' | 'Fetch' | 'Other';
86
86
 
87
+ export type RequestOverride = {
88
+ status?: number;
89
+ body?: string;
90
+ };
91
+
87
92
  export type NetworkActivityEventMap = {
88
93
  // Control events
89
94
  'network-enable': unknown;
@@ -129,6 +134,10 @@ export type NetworkActivityEventMap = {
129
134
  requestId: RequestId;
130
135
  body: string | null;
131
136
  };
137
+
138
+ 'set-overrides': {
139
+ overrides: [string, RequestOverride][];
140
+ };
132
141
  } & WebSocketEventMap &
133
142
  SSEEventMap;
134
143
 
@@ -0,0 +1,26 @@
1
+ import { forwardRef } from 'react';
2
+
3
+ export type CodeEditorProps = {
4
+ data: string | undefined;
5
+ onInput?: (event: React.FormEvent<HTMLPreElement>) => void;
6
+ };
7
+
8
+ export const CodeEditor = forwardRef<HTMLPreElement, CodeEditorProps>(
9
+ ({ data, onInput }, ref) => {
10
+ return (
11
+ <pre
12
+ ref={ref}
13
+ contentEditable
14
+ suppressContentEditableWarning
15
+ className={
16
+ 'w-full 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 ring-offset-blue-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2'
17
+ }
18
+ onInput={onInput}
19
+ >
20
+ {data}
21
+ </pre>
22
+ );
23
+ }
24
+ );
25
+
26
+ CodeEditor.displayName = 'CodeEditor';