@rozenite/network-activity-plugin 1.1.0 → 1.2.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 (50) hide show
  1. package/README.md +9 -0
  2. package/dist/App.html +1 -1
  3. package/dist/assets/{App-Kyi7zHUX.js → App-o_iVtD-5.js} +10 -4
  4. package/dist/boot-recording.cjs +1092 -0
  5. package/dist/boot-recording.js +1091 -0
  6. package/dist/react-native.cjs +3 -0
  7. package/dist/react-native.d.ts +3 -0
  8. package/dist/react-native.js +5 -1
  9. package/dist/rozenite.json +1 -1
  10. package/dist/src/react-native/boot-recording.d.ts +41 -0
  11. package/dist/src/react-native/config.d.ts +7 -4
  12. package/dist/src/react-native/events-listener.d.ts +44 -0
  13. package/dist/src/react-native/http/http-inspector.d.ts +10 -0
  14. package/dist/src/react-native/http/http-utils.d.ts +15 -0
  15. package/dist/src/react-native/inspector.d.ts +7 -0
  16. package/dist/src/react-native/network-inspector.d.ts +16 -0
  17. package/dist/src/react-native/sse/sse-inspector.d.ts +4 -7
  18. package/dist/src/react-native/useHttpInspector.d.ts +3 -0
  19. package/dist/src/react-native/useSSEInspector.d.ts +3 -0
  20. package/dist/src/react-native/useWebSocketInspector.d.ts +3 -0
  21. package/dist/src/react-native/websocket/websocket-inspector.d.ts +4 -7
  22. package/dist/src/shared/client.d.ts +3 -105
  23. package/dist/src/shared/http-events.d.ts +106 -0
  24. package/dist/src/shared/sse-events.d.ts +1 -1
  25. package/dist/src/ui/state/hooks.d.ts +3 -3
  26. package/dist/useNetworkActivityDevTools.cjs +112 -1011
  27. package/dist/useNetworkActivityDevTools.js +110 -1007
  28. package/package.json +4 -4
  29. package/react-native.ts +8 -0
  30. package/src/react-native/boot-recording.ts +90 -0
  31. package/src/react-native/config.ts +9 -4
  32. package/src/react-native/events-listener.ts +102 -0
  33. package/src/react-native/http/http-inspector.ts +174 -0
  34. package/src/react-native/http/http-utils.ts +217 -0
  35. package/src/react-native/inspector.ts +10 -0
  36. package/src/react-native/network-inspector.ts +78 -0
  37. package/src/react-native/sse/sse-inspector.ts +12 -10
  38. package/src/react-native/useHttpInspector.ts +59 -0
  39. package/src/react-native/useNetworkActivityDevTools.ts +60 -115
  40. package/src/react-native/useSSEInspector.ts +35 -0
  41. package/src/react-native/useWebSocketInspector.ts +35 -0
  42. package/src/react-native/websocket/websocket-inspector.ts +18 -10
  43. package/src/shared/client.ts +4 -140
  44. package/src/shared/http-events.ts +140 -0
  45. package/src/shared/sse-events.ts +1 -1
  46. package/src/ui/components/RequestList.tsx +9 -5
  47. package/src/ui/state/derived.ts +7 -3
  48. package/src/ui/state/store.ts +4 -3
  49. package/dist/src/react-native/http/network-inspector.d.ts +0 -8
  50. package/src/react-native/http/network-inspector.ts +0 -408
@@ -1,948 +1,89 @@
1
- import { useRef, useEffect } from "react";
1
+ import { useEffect, useRef } from "react";
2
2
  import { useRozeniteDevToolsClient } from "@rozenite/plugin-bridge";
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) {
15
- const lowerName = name.toLowerCase();
16
- for (const key in headers) {
17
- if (key.toLowerCase() === lowerName) {
18
- return { value: headers[key], originalKey: key };
19
- }
20
- }
21
- return void 0;
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
- };
52
- const REQUEST_TTL = 1e3 * 60 * 5;
53
- const getNetworkRequestsRegistry = () => {
54
- const registry = /* @__PURE__ */ new Map();
55
- const trimRegistry = () => {
56
- const now = Date.now();
57
- registry.forEach((entry) => {
58
- if (now - entry.sentAt < REQUEST_TTL) {
59
- return;
60
- }
61
- registry.delete(entry.id);
62
- });
63
- };
64
- const addEntry = (id, request) => {
65
- trimRegistry();
66
- registry.set(id, {
67
- id,
68
- request,
69
- sentAt: Date.now()
70
- });
71
- };
72
- const getEntry = (id) => {
73
- var _a;
74
- return ((_a = registry.get(id)) == null ? void 0 : _a.request) ?? null;
75
- };
76
- const clear = () => {
77
- registry.clear();
78
- };
79
- return {
80
- addEntry,
81
- getEntry,
82
- clear
83
- };
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;
107
- const originalXHROpen = XMLHttpRequest.prototype.open;
108
- const originalXHRSend = XMLHttpRequest.prototype.send;
109
- const originalXHRSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
110
- let openCallback;
111
- let sendCallback;
112
- let requestHeaderCallback;
113
- let headerReceivedCallback;
114
- let responseCallback;
115
- let overrideCallback;
116
- let isInterceptorEnabled$1 = false;
117
- const XHRInterceptor = {
118
- /**
119
- * Invoked before XMLHttpRequest.open(...) is called.
120
- */
121
- setOpenCallback(callback) {
122
- openCallback = callback;
123
- },
124
- /**
125
- * Invoked before XMLHttpRequest.send(...) is called.
126
- */
127
- setSendCallback(callback) {
128
- sendCallback = callback;
129
- },
130
- /**
131
- * Invoked after xhr's readyState becomes xhr.HEADERS_RECEIVED.
132
- */
133
- setHeaderReceivedCallback(callback) {
134
- headerReceivedCallback = callback;
135
- },
136
- /**
137
- * Invoked after xhr's readyState becomes xhr.DONE.
138
- */
139
- setResponseCallback(callback) {
140
- responseCallback = callback;
141
- },
142
- /**
143
- * Invoked before XMLHttpRequest.setRequestHeader(...) is called.
144
- */
145
- setRequestHeaderCallback(callback) {
146
- requestHeaderCallback = callback;
147
- },
148
- /**
149
- * Invoked before XMLHttpRequest.send(...) is called.
150
- */
151
- setOverrideCallback(callback) {
152
- overrideCallback = callback;
153
- },
154
- isInterceptorEnabled() {
155
- return isInterceptorEnabled$1;
156
- },
157
- enableInterception() {
158
- if (isInterceptorEnabled$1) {
159
- return;
160
- }
161
- XMLHttpRequest.prototype.open = function(method, url) {
162
- if (openCallback) {
163
- openCallback(method, url, this);
164
- }
165
- originalXHROpen.apply(this, arguments);
166
- };
167
- XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
168
- if (requestHeaderCallback) {
169
- requestHeaderCallback(header, value, this);
170
- }
171
- originalXHRSetRequestHeader.apply(this, arguments);
172
- };
173
- XMLHttpRequest.prototype.send = function(data) {
174
- if (sendCallback) {
175
- sendCallback(data, this);
176
- }
177
- if (overrideCallback) {
178
- overrideCallback(this);
179
- }
180
- if (this.addEventListener) {
181
- this.addEventListener(
182
- "readystatechange",
183
- () => {
184
- if (!isInterceptorEnabled$1) {
185
- return;
186
- }
187
- if (this.readyState === this.HEADERS_RECEIVED) {
188
- const contentTypeString = this.getResponseHeader("Content-Type");
189
- const contentLengthString = this.getResponseHeader("Content-Length");
190
- let responseContentType, responseSize;
191
- if (contentTypeString) {
192
- responseContentType = contentTypeString.split(";")[0];
193
- }
194
- if (contentLengthString) {
195
- responseSize = parseInt(contentLengthString, 10);
196
- }
197
- if (headerReceivedCallback) {
198
- headerReceivedCallback(
199
- responseContentType,
200
- responseSize,
201
- this.getAllResponseHeaders(),
202
- this
203
- );
204
- }
205
- }
206
- if (this.readyState === this.DONE) {
207
- if (responseCallback) {
208
- responseCallback(
209
- this.status,
210
- this.timeout,
211
- this.response,
212
- this.responseURL,
213
- this.responseType,
214
- this
215
- );
216
- }
217
- }
218
- },
219
- false
220
- );
221
- }
222
- originalXHRSend.apply(this, arguments);
223
- };
224
- isInterceptorEnabled$1 = true;
225
- },
226
- // Unpatch XMLHttpRequest methods and remove the callbacks.
227
- disableInterception() {
228
- if (!isInterceptorEnabled$1) {
229
- return;
230
- }
231
- isInterceptorEnabled$1 = false;
232
- XMLHttpRequest.prototype.send = originalXHRSend;
233
- XMLHttpRequest.prototype.open = originalXHROpen;
234
- XMLHttpRequest.prototype.setRequestHeader = originalXHRSetRequestHeader;
235
- responseCallback = null;
236
- openCallback = null;
237
- sendCallback = null;
238
- headerReceivedCallback = null;
239
- requestHeaderCallback = null;
240
- overrideCallback = null;
241
- }
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
- };
289
- const networkRequestsRegistry = getNetworkRequestsRegistry();
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)
297
- }
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);
331
- }
332
- if (isArrayBuffer(body)) {
333
- return getArrayBufferPostData(body);
334
- }
335
- if (isFormData(body)) {
336
- return getFormDataPostData(body);
337
- }
338
- return getTextPostData(body);
339
- };
340
- const getResponseSize = (request) => {
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;
361
- }
362
- };
363
- const getResponseBody = async (request) => {
364
- const responseType = request.responseType;
365
- if (responseType === "" || responseType === "text") {
366
- return request.responseText;
367
- }
368
- if (responseType === "blob") {
369
- const contentType = request.getResponseHeader("Content-Type") || "";
370
- if (contentType.startsWith("text/") || contentType.startsWith("application/json")) {
371
- return new Promise((resolve) => {
372
- const reader = new FileReader();
373
- reader.onload = () => {
374
- resolve(reader.result);
375
- };
376
- reader.readAsText(request.response);
377
- });
378
- }
379
- }
380
- if (responseType === "json") {
381
- return safeStringify(request.response);
382
- }
383
- return null;
384
- };
385
- const getInitiatorFromStack = () => {
386
- try {
387
- const stack = new Error().stack;
388
- if (!stack) {
389
- return { type: "other" };
390
- }
391
- const line = stack.split("\n")[9];
392
- const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
393
- if (match) {
394
- return {
395
- type: "script",
396
- url: match[2],
397
- lineNumber: parseInt(match[3]),
398
- columnNumber: parseInt(match[4])
399
- };
400
- }
401
- } catch {
402
- }
403
- return { type: "other" };
404
- };
405
- const READY_STATE_HEADERS_RECEIVED = 2;
406
- const getNetworkInspector = (pluginClient) => {
407
- const generateRequestId = () => {
408
- return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
409
- };
410
- const handleRequestSend = (data, request) => {
411
- const sendTime = Date.now();
412
- const requestId = generateRequestId();
413
- request._rozeniteRequestId = requestId;
414
- const initiator = getInitiatorFromStack();
415
- networkRequestsRegistry.addEntry(requestId, request);
416
- let ttfb = 0;
417
- pluginClient.send("request-sent", {
418
- requestId,
419
- timestamp: sendTime,
420
- request: {
421
- url: request._url,
422
- method: request._method,
423
- headers: request._headers,
424
- postData: getRequestBody(data)
425
- },
426
- type: "XHR",
427
- initiator
428
- });
429
- request.addEventListener("progress", (event) => {
430
- pluginClient.send("request-progress", {
431
- requestId,
432
- timestamp: Date.now(),
433
- loaded: event.loaded,
434
- total: event.total,
435
- lengthComputable: event.lengthComputable
436
- });
437
- });
438
- request.addEventListener("readystatechange", () => {
439
- if (request.readyState === READY_STATE_HEADERS_RECEIVED) {
440
- ttfb = Date.now() - sendTime;
441
- }
442
- });
443
- request.addEventListener("load", () => {
444
- pluginClient.send("response-received", {
445
- requestId,
446
- timestamp: Date.now(),
447
- type: "XHR",
448
- response: {
449
- url: request._url,
450
- status: request.status,
451
- statusText: request.statusText,
452
- headers: applyReactNativeResponseHeadersLogic(
453
- request.responseHeaders || {}
454
- ),
455
- contentType: getContentType(request),
456
- size: getResponseSize(request),
457
- responseTime: Date.now()
458
- }
459
- });
460
- });
461
- request.addEventListener("loadend", () => {
462
- pluginClient.send("request-completed", {
463
- requestId,
464
- timestamp: Date.now(),
465
- duration: Date.now() - sendTime,
466
- size: getResponseSize(request),
467
- ttfb
468
- });
469
- });
470
- request.addEventListener("error", () => {
471
- pluginClient.send("request-failed", {
472
- requestId,
473
- timestamp: Date.now(),
474
- type: "XHR",
475
- error: "Failed",
476
- canceled: false
477
- });
478
- });
479
- request.addEventListener("abort", () => {
480
- pluginClient.send("request-failed", {
481
- requestId,
482
- timestamp: Date.now(),
483
- type: "XHR",
484
- error: "Aborted",
485
- canceled: true
486
- });
487
- });
488
- request.addEventListener("timeout", () => {
489
- pluginClient.send("request-failed", {
490
- requestId,
491
- timestamp: Date.now(),
492
- type: "XHR",
493
- error: "Timeout",
494
- canceled: false
495
- });
496
- });
497
- };
498
- const handleRequestOverride = (request) => {
499
- const override = overridesRegistry$1.getOverrideForUrl(
500
- request._url
501
- );
502
- if (!override) {
3
+ import { g as getOverridesRegistry, a as getResponseBody, c as createNetworkInspectorsConfiguration, D as DEFAULT_CONFIG, v as validateConfig, i as isHttpEvent, b as isWebSocketEvent, d as isSSEEvent } from "./boot-recording.js";
4
+ const overridesRegistry = getOverridesRegistry();
5
+ const useHttpInspector = (client, httpInspector, isEnabled, isRecordingEnabled) => {
6
+ useEffect(() => {
7
+ if (!client || !isEnabled) {
503
8
  return;
504
9
  }
505
- request.addEventListener("readystatechange", () => {
506
- if (override.body !== void 0) {
507
- Object.defineProperty(request, "responseType", {
508
- writable: true
509
- });
510
- Object.defineProperty(request, "response", {
511
- writable: true
512
- });
513
- Object.defineProperty(request, "responseText", {
514
- writable: true
515
- });
516
- const contentType = getContentType(request);
517
- if (contentType === "application/json") {
518
- request.responseType = "json";
519
- } else if (contentType === "text/plain") {
520
- request.responseType = "text";
10
+ const networkRequestsRegistry = httpInspector.getNetworkRequestsRegistry();
11
+ const subscriptions = [
12
+ client.onMessage("network-enable", () => {
13
+ httpInspector.enable();
14
+ }),
15
+ client.onMessage("network-disable", () => {
16
+ httpInspector.disable();
17
+ }),
18
+ client.onMessage("set-overrides", (data) => {
19
+ overridesRegistry.setOverrides(data.overrides);
20
+ }),
21
+ client.onMessage("get-response-body", async ({ requestId }) => {
22
+ const request = networkRequestsRegistry.getEntry(requestId);
23
+ if (!request) {
24
+ return;
521
25
  }
522
- request.response = override.body;
523
- request.responseText = override.body;
524
- }
525
- if (override.status !== void 0) {
526
- Object.defineProperty(request, "status", {
527
- writable: true
26
+ const body = await getResponseBody(request);
27
+ client.send("response-body", {
28
+ requestId,
29
+ body
528
30
  });
529
- request.status = override.status;
530
- }
531
- });
532
- };
533
- const enable = () => {
534
- XHRInterceptor.disableInterception();
535
- XHRInterceptor.setSendCallback(handleRequestSend);
536
- XHRInterceptor.setOverrideCallback(handleRequestOverride);
537
- XHRInterceptor.enableInterception();
538
- };
539
- const disable = () => {
540
- XHRInterceptor.disableInterception();
541
- networkRequestsRegistry.clear();
542
- };
543
- const isEnabled = () => {
544
- return XHRInterceptor.isInterceptorEnabled();
545
- };
546
- const enableSubscription = pluginClient.onMessage("network-enable", () => {
547
- enable();
548
- });
549
- const disableSubscription = pluginClient.onMessage("network-disable", () => {
550
- disable();
551
- });
552
- const handleBodySubscription = pluginClient.onMessage(
553
- "get-response-body",
554
- async ({ requestId }) => {
555
- const request = networkRequestsRegistry.getEntry(requestId);
556
- if (!request) {
557
- return;
558
- }
559
- const body = await getResponseBody(request);
560
- pluginClient.send("response-body", {
561
- requestId,
562
- body
563
- });
31
+ })
32
+ ];
33
+ if (isRecordingEnabled) {
34
+ httpInspector.enable();
564
35
  }
565
- );
566
- const dispose = () => {
567
- disable();
568
- enableSubscription.remove();
569
- disableSubscription.remove();
570
- handleBodySubscription.remove();
571
- };
572
- return {
573
- enable,
574
- disable,
575
- isEnabled,
576
- dispose
577
- };
578
- };
579
- const getWebSocketInterceptor = () => {
580
- if (Platform.constants.reactNativeVersion.minor >= 79) {
581
- return WebSocketInterceptor;
582
- } else {
583
- const WebSocketInterceptorPreRN079 = WebSocketInterceptor;
584
- return {
585
- ...WebSocketInterceptorPreRN079,
586
- setOnMessageCallback: (callback) => {
587
- WebSocketInterceptorPreRN079.setOnMessageCallback((socketId, data) => {
588
- callback(data, socketId);
589
- });
590
- },
591
- setOnCloseCallback: (callback) => {
592
- WebSocketInterceptorPreRN079.setOnCloseCallback((error, socketId) => {
593
- callback(socketId, error);
594
- });
595
- },
596
- setOnErrorCallback: (callback) => {
597
- WebSocketInterceptorPreRN079.setOnErrorCallback((error, socketId) => {
598
- callback(socketId, error);
599
- });
600
- }
36
+ return () => {
37
+ subscriptions.forEach((subscription) => subscription.remove());
38
+ httpInspector.dispose();
601
39
  };
602
- }
40
+ }, [client, httpInspector, isEnabled, isRecordingEnabled]);
603
41
  };
604
- const getWebSocketInspector = () => {
605
- const eventEmitter = createNanoEvents();
606
- const socketUrlMap = /* @__PURE__ */ new Map();
607
- const webSocketInterceptor = getWebSocketInterceptor();
608
- return {
609
- enable: () => {
610
- webSocketInterceptor.setConnectCallback(
611
- (url, protocols, options, socketId) => {
612
- socketUrlMap.set(socketId, url);
613
- const event = {
614
- type: "websocket-connect",
615
- url,
616
- socketId,
617
- timestamp: Date.now(),
618
- protocols,
619
- options
620
- };
621
- eventEmitter.emit("websocket-connect", event);
622
- }
623
- );
624
- webSocketInterceptor.setCloseCallback(
625
- (code, reason, socketId) => {
626
- const url = socketUrlMap.get(socketId);
627
- if (!url) {
628
- return;
629
- }
630
- const event = {
631
- type: "websocket-close",
632
- url,
633
- socketId,
634
- timestamp: Date.now(),
635
- code: code || 0,
636
- reason: reason || void 0
637
- };
638
- eventEmitter.emit("websocket-close", event);
639
- socketUrlMap.delete(socketId);
640
- }
641
- );
642
- webSocketInterceptor.setOnMessageCallback(
643
- (data, socketId) => {
644
- const url = socketUrlMap.get(socketId);
645
- if (!url) {
646
- return;
647
- }
648
- const event = {
649
- type: "websocket-message-received",
650
- url,
651
- socketId,
652
- timestamp: Date.now(),
653
- data,
654
- messageType: typeof data === "string" ? "text" : "binary"
655
- };
656
- eventEmitter.emit("websocket-message-received", event);
657
- }
658
- );
659
- webSocketInterceptor.setOnErrorCallback(
660
- (error, socketId) => {
661
- const url = socketUrlMap.get(socketId);
662
- if (!url) {
663
- return;
664
- }
665
- const event = {
666
- type: "websocket-error",
667
- url,
668
- socketId,
669
- timestamp: Date.now(),
670
- error
671
- };
672
- eventEmitter.emit("websocket-error", event);
673
- }
674
- );
675
- webSocketInterceptor.setSendCallback((data, socketId) => {
676
- const url = socketUrlMap.get(socketId);
677
- if (!url) {
678
- return;
679
- }
680
- const event = {
681
- type: "websocket-message-sent",
682
- url,
683
- socketId,
684
- timestamp: Date.now(),
685
- data,
686
- messageType: typeof data === "string" ? "text" : "binary"
687
- };
688
- eventEmitter.emit("websocket-message-sent", event);
689
- });
690
- webSocketInterceptor.setOnOpenCallback((socketId) => {
691
- const url = socketUrlMap.get(socketId);
692
- if (!url) {
693
- return;
694
- }
695
- const event = {
696
- type: "websocket-open",
697
- url,
698
- socketId,
699
- timestamp: Date.now()
700
- };
701
- eventEmitter.emit("websocket-open", event);
702
- });
703
- webSocketInterceptor.setOnCloseCallback(
704
- (error, socketId) => {
705
- const url = socketUrlMap.get(socketId);
706
- if (!url) {
707
- return;
708
- }
709
- const event = {
710
- type: "websocket-close",
711
- url,
712
- socketId,
713
- timestamp: Date.now(),
714
- code: error.code,
715
- reason: error.reason
716
- };
717
- eventEmitter.emit("websocket-close", event);
718
- socketUrlMap.delete(socketId);
719
- }
720
- );
721
- webSocketInterceptor.enableInterception();
722
- },
723
- disable: () => {
724
- webSocketInterceptor.disableInterception();
725
- },
726
- isEnabled: () => webSocketInterceptor.isInterceptorEnabled(),
727
- dispose: () => {
728
- eventEmitter.events = {};
729
- socketUrlMap.clear();
730
- },
731
- on: (event, callback) => eventEmitter.on(event, callback)
732
- };
733
- };
734
- let connectCallback;
735
- let messageCallback;
736
- let errorCallback;
737
- let openEventCallback;
738
- let closeCallback;
739
- let isInterceptorEnabled = false;
740
- const eventSourceClass = getEventSource();
741
- const originalOpen = eventSourceClass.prototype.open;
742
- const originalDispatch = eventSourceClass.prototype.dispatch;
743
- const BUILT_IN_EVENT_TYPES = /* @__PURE__ */ new Set(["open", "error", "close", "done"]);
744
- const SSEInterceptor = {
745
- /**
746
- * Invoked when EventSource.open() is called (connection attempt starting).
747
- */
748
- setConnectCallback(callback) {
749
- connectCallback = callback;
750
- },
751
- /**
752
- * Invoked when a message event is received.
753
- */
754
- setMessageCallback(callback) {
755
- messageCallback = callback;
756
- },
757
- /**
758
- * Invoked when an error event occurs.
759
- */
760
- setErrorCallback(callback) {
761
- errorCallback = callback;
762
- },
763
- /**
764
- * Invoked when the connection is successfully opened (open event fired).
765
- */
766
- setOpenEventCallback(callback) {
767
- openEventCallback = callback;
768
- },
769
- /**
770
- * Invoked when the connection is closed.
771
- */
772
- setCloseCallback(callback) {
773
- closeCallback = callback;
774
- },
775
- isInterceptorEnabled() {
776
- return isInterceptorEnabled;
777
- },
778
- enableInterception() {
779
- if (isInterceptorEnabled) {
42
+ const useWebSocketInspector = (client, websocketInspector, isEnabled, isRecordingEnabled) => {
43
+ useEffect(() => {
44
+ if (!client || !isEnabled) {
780
45
  return;
781
46
  }
782
- eventSourceClass.prototype.open = function() {
783
- if (connectCallback) {
784
- connectCallback(this.url, this);
785
- }
786
- this.addEventListener("open", (event) => {
787
- if (openEventCallback) {
788
- openEventCallback(event, this);
789
- }
790
- });
791
- this.addEventListener(
792
- "error",
793
- (event) => {
794
- if (errorCallback) {
795
- errorCallback(event, this);
796
- }
797
- }
798
- );
799
- this.addEventListener("close", (event) => {
800
- if (closeCallback) {
801
- closeCallback(event, this);
802
- }
803
- });
804
- return originalOpen.call(this);
805
- };
806
- eventSourceClass.prototype.dispatch = function(eventType, data) {
807
- if (!BUILT_IN_EVENT_TYPES.has(eventType)) {
808
- if (messageCallback) {
809
- messageCallback(data, this);
810
- }
811
- }
812
- return originalDispatch.call(this, eventType, data);
47
+ const subscriptions = [
48
+ client.onMessage("network-enable", () => {
49
+ websocketInspector.enable();
50
+ }),
51
+ client.onMessage("network-disable", () => {
52
+ websocketInspector.disable();
53
+ })
54
+ ];
55
+ if (isRecordingEnabled) {
56
+ websocketInspector.enable();
57
+ }
58
+ return () => {
59
+ subscriptions.forEach((subscription) => subscription.remove());
60
+ websocketInspector.dispose();
813
61
  };
814
- isInterceptorEnabled = true;
815
- },
816
- // Unpatch EventSource open method and remove the callbacks.
817
- disableInterception() {
818
- if (!isInterceptorEnabled) {
62
+ }, [client, websocketInspector, isEnabled, isRecordingEnabled]);
63
+ };
64
+ const useSSEInspector = (client, sseInspector, isEnabled, isRecordingEnabled) => {
65
+ useEffect(() => {
66
+ if (!client || !isEnabled) {
819
67
  return;
820
68
  }
821
- isInterceptorEnabled = false;
822
- eventSourceClass.prototype.open = originalOpen;
823
- eventSourceClass.prototype.dispatch = originalDispatch;
824
- connectCallback = null;
825
- messageCallback = null;
826
- errorCallback = null;
827
- openEventCallback = null;
828
- closeCallback = null;
829
- }
830
- };
831
- const getSSEInspector = () => {
832
- const eventEmitter = createNanoEvents();
833
- const getRequestId = (eventSource) => {
834
- var _a;
835
- const requestId = (_a = eventSource._xhr) == null ? void 0 : _a._rozeniteRequestId;
836
- if (!requestId) {
837
- return null;
69
+ const subscriptions = [
70
+ client.onMessage("network-enable", () => {
71
+ sseInspector.enable();
72
+ }),
73
+ client.onMessage("network-disable", () => {
74
+ sseInspector.disable();
75
+ })
76
+ ];
77
+ if (isRecordingEnabled) {
78
+ sseInspector.enable();
838
79
  }
839
- return requestId;
840
- };
841
- return {
842
- enable: () => {
843
- SSEInterceptor.setOpenEventCallback((_, eventSource) => {
844
- const sseEventSource = eventSource;
845
- const requestId = getRequestId(sseEventSource);
846
- if (!requestId) {
847
- return;
848
- }
849
- const sseXhr = sseEventSource._xhr;
850
- const event = {
851
- type: "sse-open",
852
- requestId,
853
- timestamp: Date.now(),
854
- response: {
855
- url: sseXhr._url,
856
- status: sseXhr.status,
857
- statusText: sseXhr.statusText,
858
- headers: sseXhr.responseHeaders || {},
859
- contentType: getContentType(sseXhr),
860
- size: 0,
861
- responseTime: Date.now()
862
- }
863
- };
864
- eventEmitter.emit("sse-open", event);
865
- });
866
- SSEInterceptor.setMessageCallback((messageEvent, eventSource) => {
867
- const sseEventSource = eventSource;
868
- const requestId = getRequestId(sseEventSource);
869
- if (!requestId) {
870
- return;
871
- }
872
- const event = {
873
- type: "sse-message",
874
- requestId,
875
- timestamp: Date.now(),
876
- payload: {
877
- type: messageEvent.type,
878
- data: messageEvent.data || ""
879
- }
880
- };
881
- eventEmitter.emit("sse-message", event);
882
- });
883
- SSEInterceptor.setErrorCallback((errorEvent, eventSource) => {
884
- const sseEventSource = eventSource;
885
- const requestId = getRequestId(sseEventSource);
886
- if (!requestId) {
887
- return;
888
- }
889
- const event = {
890
- type: "sse-error",
891
- requestId,
892
- timestamp: Date.now(),
893
- error: {
894
- type: errorEvent.type,
895
- message: errorEvent.type === "timeout" ? "Timeout" : errorEvent.message
896
- }
897
- };
898
- eventEmitter.emit("sse-error", event);
899
- });
900
- SSEInterceptor.setCloseCallback((_, eventSource) => {
901
- const sseEventSource = eventSource;
902
- const requestId = getRequestId(sseEventSource);
903
- if (!requestId) {
904
- return;
905
- }
906
- const event = {
907
- type: "sse-close",
908
- requestId,
909
- timestamp: Date.now()
910
- };
911
- eventEmitter.emit("sse-close", event);
912
- });
913
- SSEInterceptor.enableInterception();
914
- },
915
- disable: () => {
916
- SSEInterceptor.disableInterception();
917
- },
918
- isEnabled: () => SSEInterceptor.isInterceptorEnabled(),
919
- dispose: () => {
920
- SSEInterceptor.disableInterception();
921
- eventEmitter.events = {};
922
- },
923
- on: (event, callback) => eventEmitter.on(event, callback)
924
- };
925
- };
926
- const DEFAULT_CONFIG = {
927
- inspectors: {
928
- http: true,
929
- websocket: true,
930
- sse: true
931
- },
932
- clientUISettings: {
933
- showUrlAsName: false
934
- }
935
- };
936
- const validateConfig = (config) => {
937
- const inspectors = config.inspectors;
938
- if (!inspectors) {
939
- return;
940
- }
941
- if (inspectors.sse && !inspectors.http) {
942
- throw new Error("SSE inspector requires HTTP inspector to be enabled.");
943
- }
80
+ return () => {
81
+ subscriptions.forEach((subscription) => subscription.remove());
82
+ sseInspector.dispose();
83
+ };
84
+ }, [client, sseInspector, isEnabled, isRecordingEnabled]);
944
85
  };
945
- const overridesRegistry = getOverridesRegistry();
86
+ const inspectorsConfig = createNetworkInspectorsConfiguration();
946
87
  const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
947
88
  var _a, _b, _c, _d;
948
89
  const isRecordingEnabledRef = useRef(false);
@@ -953,6 +94,7 @@ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
953
94
  const isWebSocketInspectorEnabled = ((_b = config.inspectors) == null ? void 0 : _b.websocket) ?? true;
954
95
  const isSSEInspectorEnabled = ((_c = config.inspectors) == null ? void 0 : _c.sse) ?? true;
955
96
  const showUrlAsName = (_d = config.clientUISettings) == null ? void 0 : _d.showUrlAsName;
97
+ const { eventsListener, networkInspector } = inspectorsConfig;
956
98
  useEffect(() => {
957
99
  if (!client) {
958
100
  return;
@@ -974,13 +116,23 @@ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
974
116
  const subscriptions = [
975
117
  client.onMessage("network-enable", () => {
976
118
  isRecordingEnabledRef.current = true;
119
+ eventsListener.connect(client.send, (message) => {
120
+ const type = message.type;
121
+ if (isHttpEvent(type)) {
122
+ return isHttpInspectorEnabled;
123
+ }
124
+ if (isWebSocketEvent(type)) {
125
+ return isWebSocketInspectorEnabled;
126
+ }
127
+ if (isSSEEvent(type)) {
128
+ return isSSEInspectorEnabled;
129
+ }
130
+ return true;
131
+ });
977
132
  }),
978
133
  client.onMessage("network-disable", () => {
979
134
  isRecordingEnabledRef.current = false;
980
135
  }),
981
- client.onMessage("set-overrides", (data) => {
982
- overridesRegistry.setOverrides(data.overrides);
983
- }),
984
136
  client.onMessage("get-client-ui-settings", () => {
985
137
  sendClientUISettings();
986
138
  })
@@ -989,80 +141,31 @@ const useNetworkActivityDevTools = (config = DEFAULT_CONFIG) => {
989
141
  return () => {
990
142
  subscriptions.forEach((subscription) => subscription.remove());
991
143
  };
992
- }, [client, showUrlAsName]);
993
- useEffect(() => {
994
- if (!client || !isHttpInspectorEnabled) {
995
- return;
996
- }
997
- const networkInspector = getNetworkInspector(client);
998
- if (isRecordingEnabledRef.current) {
999
- networkInspector.enable();
1000
- }
1001
- return () => {
1002
- networkInspector.dispose();
1003
- };
1004
- }, [client, isHttpInspectorEnabled]);
1005
- useEffect(() => {
1006
- if (!client || !isWebSocketInspectorEnabled) {
1007
- return;
1008
- }
1009
- const eventsToForward = [
1010
- "websocket-connect",
1011
- "websocket-open",
1012
- "websocket-close",
1013
- "websocket-message-sent",
1014
- "websocket-message-received",
1015
- "websocket-error",
1016
- "websocket-connection-status-changed"
1017
- ];
1018
- const websocketInspector = getWebSocketInspector();
1019
- eventsToForward.forEach((event) => {
1020
- websocketInspector.on(event, (event2) => {
1021
- client.send(event2.type, event2);
1022
- });
1023
- });
1024
- client.onMessage("network-enable", () => {
1025
- websocketInspector.enable();
1026
- });
1027
- client.onMessage("network-disable", () => {
1028
- websocketInspector.disable();
1029
- });
1030
- if (isRecordingEnabledRef.current) {
1031
- websocketInspector.enable();
1032
- }
1033
- return () => {
1034
- websocketInspector.dispose();
1035
- };
1036
- }, [client, isWebSocketInspectorEnabled]);
1037
- useEffect(() => {
1038
- if (!client || !isSSEInspectorEnabled) {
1039
- return;
1040
- }
1041
- const eventsToForward = [
1042
- "sse-open",
1043
- "sse-message",
1044
- "sse-error",
1045
- "sse-close"
1046
- ];
1047
- const sseInspector = getSSEInspector();
1048
- eventsToForward.forEach((event) => {
1049
- sseInspector.on(event, (event2) => {
1050
- client.send(event2.type, event2);
1051
- });
1052
- });
1053
- client.onMessage("network-enable", () => {
1054
- sseInspector.enable();
1055
- });
1056
- client.onMessage("network-disable", () => {
1057
- sseInspector.disable();
1058
- });
1059
- if (isRecordingEnabledRef.current) {
1060
- sseInspector.enable();
1061
- }
1062
- return () => {
1063
- sseInspector.dispose();
1064
- };
1065
- }, [client, isSSEInspectorEnabled]);
144
+ }, [
145
+ client,
146
+ showUrlAsName,
147
+ isHttpInspectorEnabled,
148
+ isWebSocketInspectorEnabled,
149
+ isSSEInspectorEnabled
150
+ ]);
151
+ useHttpInspector(
152
+ client,
153
+ networkInspector.http,
154
+ isHttpInspectorEnabled,
155
+ isRecordingEnabledRef.current
156
+ );
157
+ useWebSocketInspector(
158
+ client,
159
+ networkInspector.websocket,
160
+ isWebSocketInspectorEnabled,
161
+ isRecordingEnabledRef.current
162
+ );
163
+ useSSEInspector(
164
+ client,
165
+ networkInspector.sse,
166
+ isSSEInspectorEnabled,
167
+ isRecordingEnabledRef.current
168
+ );
1066
169
  return client;
1067
170
  };
1068
171
  export {