@rozenite/network-activity-plugin 1.5.1 → 1.7.0-rc.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 (113) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +2 -0
  3. package/dist/{App.html → devtools/App.html} +2 -2
  4. package/dist/{boot-recording.cjs → react-native/chunks/boot-recording.cjs} +1 -1
  5. package/dist/{boot-recording.js → react-native/chunks/boot-recording.js} +3 -3
  6. package/dist/react-native/chunks/boot-recording.require.cjs +5 -0
  7. package/dist/react-native/chunks/boot-recording.require.js +5 -0
  8. package/dist/react-native/chunks/useNetworkActivityDevTools.require.cjs +1102 -0
  9. package/dist/react-native/chunks/useNetworkActivityDevTools.require.js +1102 -0
  10. package/dist/{react-native.cjs → react-native/index.cjs} +2 -2
  11. package/dist/react-native/index.d.ts +305 -0
  12. package/dist/{react-native.js → react-native/index.js} +2 -2
  13. package/dist/rozenite.json +1 -1
  14. package/package.json +27 -7
  15. package/src/react-native/agent/__tests__/network-activity-agent-state.test.ts +250 -0
  16. package/src/react-native/agent/state.ts +869 -0
  17. package/src/react-native/agent/tools.ts +146 -0
  18. package/src/react-native/agent/use-network-activity-agent-tools.ts +244 -0
  19. package/src/react-native/http/http-inspector.ts +0 -1
  20. package/src/react-native/useNetworkActivityDevTools.ts +11 -0
  21. package/tsconfig.json +3 -0
  22. package/dist/react-native.d.ts +0 -4
  23. package/dist/rozenite.config.d.ts +0 -7
  24. package/dist/src/react-native/boot-recording.d.ts +0 -41
  25. package/dist/src/react-native/config.d.ts +0 -23
  26. package/dist/src/react-native/events-listener.d.ts +0 -44
  27. package/dist/src/react-native/http/http-inspector.d.ts +0 -10
  28. package/dist/src/react-native/http/http-utils.d.ts +0 -15
  29. package/dist/src/react-native/http/network-requests-registry.d.ts +0 -6
  30. package/dist/src/react-native/http/overrides-registry.d.ts +0 -6
  31. package/dist/src/react-native/http/xhr-interceptor.d.ts +0 -44
  32. package/dist/src/react-native/inspector.d.ts +0 -7
  33. package/dist/src/react-native/network-inspector.d.ts +0 -16
  34. package/dist/src/react-native/sse/event-source.d.ts +0 -2
  35. package/dist/src/react-native/sse/sse-inspector.d.ts +0 -6
  36. package/dist/src/react-native/sse/sse-interceptor.d.ts +0 -36
  37. package/dist/src/react-native/sse/types.d.ts +0 -6
  38. package/dist/src/react-native/useHttpInspector.d.ts +0 -3
  39. package/dist/src/react-native/useNetworkActivityDevTools.d.ts +0 -3
  40. package/dist/src/react-native/useSSEInspector.d.ts +0 -3
  41. package/dist/src/react-native/useWebSocketInspector.d.ts +0 -3
  42. package/dist/src/react-native/utils/getBlobName.d.ts +0 -35
  43. package/dist/src/react-native/utils/getFormDataEntries.d.ts +0 -18
  44. package/dist/src/react-native/utils.d.ts +0 -6
  45. package/dist/src/react-native/websocket/websocket-inspector.d.ts +0 -6
  46. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +0 -73
  47. package/dist/src/shared/client.d.ts +0 -17
  48. package/dist/src/shared/http-events.d.ts +0 -106
  49. package/dist/src/shared/sse-events.d.ts +0 -38
  50. package/dist/src/shared/websocket-events.d.ts +0 -60
  51. package/dist/src/ui/App.d.ts +0 -1
  52. package/dist/src/ui/components/Badge.d.ts +0 -9
  53. package/dist/src/ui/components/Button.d.ts +0 -11
  54. package/dist/src/ui/components/CodeBlock.d.ts +0 -3
  55. package/dist/src/ui/components/CodeEditor.d.ts +0 -5
  56. package/dist/src/ui/components/CookieCard.d.ts +0 -7
  57. package/dist/src/ui/components/CopyRequestDropdown.d.ts +0 -7
  58. package/dist/src/ui/components/DropdownMenu.d.ts +0 -27
  59. package/dist/src/ui/components/FilterBar.d.ts +0 -10
  60. package/dist/src/ui/components/Input.d.ts +0 -3
  61. package/dist/src/ui/components/JsonTree.d.ts +0 -5
  62. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +0 -7
  63. package/dist/src/ui/components/KeyValueGrid.d.ts +0 -13
  64. package/dist/src/ui/components/OverrideResponse.d.ts +0 -8
  65. package/dist/src/ui/components/RequestBody.d.ts +0 -6
  66. package/dist/src/ui/components/RequestList.d.ts +0 -30
  67. package/dist/src/ui/components/ScrollArea.d.ts +0 -5
  68. package/dist/src/ui/components/Section.d.ts +0 -8
  69. package/dist/src/ui/components/Separator.d.ts +0 -4
  70. package/dist/src/ui/components/SidePanel.d.ts +0 -1
  71. package/dist/src/ui/components/Tabs.d.ts +0 -7
  72. package/dist/src/ui/components/Toolbar.d.ts +0 -1
  73. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +0 -4
  74. package/dist/src/ui/state/derived.d.ts +0 -5
  75. package/dist/src/ui/state/hooks.d.ts +0 -21
  76. package/dist/src/ui/state/model.d.ts +0 -113
  77. package/dist/src/ui/state/store.d.ts +0 -48
  78. package/dist/src/ui/tabs/CookiesTab.d.ts +0 -5
  79. package/dist/src/ui/tabs/HeadersTab.d.ts +0 -5
  80. package/dist/src/ui/tabs/MessagesTab.d.ts +0 -5
  81. package/dist/src/ui/tabs/RequestTab.d.ts +0 -5
  82. package/dist/src/ui/tabs/ResponseTab.d.ts +0 -6
  83. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +0 -5
  84. package/dist/src/ui/tabs/TimingTab.d.ts +0 -5
  85. package/dist/src/ui/types.d.ts +0 -26
  86. package/dist/src/ui/utils/assert.d.ts +0 -1
  87. package/dist/src/ui/utils/checkRequestBodyBinary.d.ts +0 -2
  88. package/dist/src/ui/utils/cn.d.ts +0 -2
  89. package/dist/src/ui/utils/copyToClipboard.d.ts +0 -1
  90. package/dist/src/ui/utils/escapeShellArg.d.ts +0 -1
  91. package/dist/src/ui/utils/generateCurlCommand.d.ts +0 -2
  92. package/dist/src/ui/utils/generateFetchCall.d.ts +0 -2
  93. package/dist/src/ui/utils/generateMultipartBody.d.ts +0 -4
  94. package/dist/src/ui/utils/getId.d.ts +0 -1
  95. package/dist/src/ui/utils/getStatusColor.d.ts +0 -1
  96. package/dist/src/ui/views/InspectorView.d.ts +0 -5
  97. package/dist/src/ui/views/LoadingView.d.ts +0 -1
  98. package/dist/src/utils/applyReactNativeRequestHeadersLogic.d.ts +0 -7
  99. package/dist/src/utils/applyReactNativeResponseHeadersLogic.d.ts +0 -9
  100. package/dist/src/utils/cookieParser.d.ts +0 -6
  101. package/dist/src/utils/getContentTypeMimeType.d.ts +0 -2
  102. package/dist/src/utils/getHttpHeader.d.ts +0 -5
  103. package/dist/src/utils/getHttpHeaderValueAsString.d.ts +0 -11
  104. package/dist/src/utils/getStringSizeInBytes.d.ts +0 -1
  105. package/dist/src/utils/inferContentTypeFromPostData.d.ts +0 -2
  106. package/dist/src/utils/safeStringify.d.ts +0 -1
  107. package/dist/src/utils/typeChecks.d.ts +0 -9
  108. package/dist/useNetworkActivityDevTools.cjs +0 -171
  109. package/dist/useNetworkActivityDevTools.js +0 -171
  110. /package/dist/{assets → devtools/assets}/App-BrSkOkws.css +0 -0
  111. /package/dist/{assets/App-CGt4qucR.js → devtools/assets/App-pokLiGYV.js} +0 -0
  112. /package/dist/{event-source.cjs → react-native/chunks/event-source.cjs} +0 -0
  113. /package/dist/{event-source.js → react-native/chunks/event-source.js} +0 -0
@@ -0,0 +1,1102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const react = require("react");
4
+ const pluginBridge = require("@rozenite/plugin-bridge");
5
+ const bootRecording = require("./boot-recording.cjs");
6
+ const agentBridge = require("@rozenite/agent-bridge");
7
+ const DEFAULT_PAGE_LIMIT = 20;
8
+ const MAX_PAGE_LIMIT = 100;
9
+ const HTTP_BUFFER_CAPACITY = 500;
10
+ const REALTIME_BUFFER_CAPACITY = 200;
11
+ const createInitialState = () => ({
12
+ isRecording: false,
13
+ generation: 0,
14
+ httpOrder: [],
15
+ httpRecords: /* @__PURE__ */ new Map(),
16
+ httpTotalRecorded: 0,
17
+ httpEvictedCount: 0,
18
+ httpTruncated: false,
19
+ realtimeOrder: [],
20
+ realtimeRecords: /* @__PURE__ */ new Map(),
21
+ realtimeTotalRecorded: 0,
22
+ realtimeEvictedCount: 0,
23
+ realtimeTruncated: false,
24
+ nextMessageId: 1,
25
+ recordingMetadata: {
26
+ enabledInspectors: {
27
+ http: true,
28
+ websocket: true,
29
+ sse: true
30
+ }
31
+ }
32
+ });
33
+ const getLimit = (value) => {
34
+ if (typeof value !== "number" || !Number.isInteger(value) || !Number.isFinite(value) || value < 1) {
35
+ return DEFAULT_PAGE_LIMIT;
36
+ }
37
+ return Math.min(value, MAX_PAGE_LIMIT);
38
+ };
39
+ const encodeCursor = (scope, index) => {
40
+ return `${scope}:${index}`;
41
+ };
42
+ const decodeCursor = (cursor, scope) => {
43
+ const [cursorScope, rawIndex] = cursor.split(":", 2);
44
+ if (cursorScope !== scope || !rawIndex) {
45
+ throw new Error(
46
+ "Cursor does not match the requested listing. Run the command again."
47
+ );
48
+ }
49
+ const index = Number(rawIndex);
50
+ if (!Number.isInteger(index) || index < 0) {
51
+ throw new Error("Cursor is invalid. Run the command again.");
52
+ }
53
+ return index;
54
+ };
55
+ const paginate = (rows, scope, limit, cursor) => {
56
+ const startIndex = cursor ? decodeCursor(cursor, scope) : 0;
57
+ const endIndex = Math.min(startIndex + limit, rows.length);
58
+ const hasMore = endIndex < rows.length;
59
+ return {
60
+ items: rows.slice(startIndex, endIndex),
61
+ page: {
62
+ limit,
63
+ hasMore,
64
+ ...hasMore ? { nextCursor: encodeCursor(scope, endIndex) } : {}
65
+ }
66
+ };
67
+ };
68
+ const serializeRequestBody = (requestId, postData) => {
69
+ if (!postData) {
70
+ return {
71
+ requestId,
72
+ available: false,
73
+ reason: "No request body is available for this request."
74
+ };
75
+ }
76
+ if (postData.type === "text") {
77
+ return {
78
+ requestId,
79
+ available: true,
80
+ body: postData.value,
81
+ base64Encoded: false
82
+ };
83
+ }
84
+ return {
85
+ requestId,
86
+ available: true,
87
+ body: bootRecording.safeStringify(postData),
88
+ base64Encoded: false
89
+ };
90
+ };
91
+ const createHttpSummary = (record) => ({
92
+ requestId: record.requestId,
93
+ method: record.request.method,
94
+ url: record.request.url,
95
+ status: record.response?.status ?? null,
96
+ type: record.resourceType,
97
+ startTimeMs: record.startTimeMs,
98
+ endTimeMs: record.endTimeMs ?? null,
99
+ durationMs: record.durationMs ?? null,
100
+ transferSize: record.size ?? null,
101
+ encodedDataLength: record.response?.size ?? null,
102
+ outcome: record.status === "failed" ? "failed" : record.status === "finished" ? "success" : "in-flight"
103
+ });
104
+ const getRealtimeSummary = (record) => {
105
+ if (record.kind === "websocket") {
106
+ return {
107
+ requestId: record.requestId,
108
+ kind: record.kind,
109
+ url: record.url,
110
+ status: record.status,
111
+ startedAt: record.startedAt,
112
+ endedAt: record.endedAt ?? null,
113
+ durationMs: record.durationMs ?? null,
114
+ messageCount: record.messages.length,
115
+ error: record.error ?? null,
116
+ closeCode: record.closeCode ?? null
117
+ };
118
+ }
119
+ return {
120
+ requestId: record.requestId,
121
+ kind: record.kind,
122
+ url: record.request?.url ?? record.response?.url ?? null,
123
+ status: record.status,
124
+ startedAt: record.startedAt,
125
+ endedAt: record.endedAt ?? null,
126
+ durationMs: record.durationMs ?? null,
127
+ messageCount: record.messages.length,
128
+ error: record.error ?? null,
129
+ httpStatus: record.response?.status ?? null
130
+ };
131
+ };
132
+ const trimMap = (order, records, capacity) => {
133
+ let evicted = 0;
134
+ while (order.length > capacity) {
135
+ const oldestId = order.shift();
136
+ if (!oldestId) {
137
+ break;
138
+ }
139
+ records.delete(oldestId);
140
+ evicted += 1;
141
+ }
142
+ return evicted;
143
+ };
144
+ const createNetworkActivityAgentState = () => {
145
+ const state = createInitialState();
146
+ const getStatus = () => ({
147
+ recording: {
148
+ isRecording: state.isRecording,
149
+ startedAt: state.startedAt ?? null,
150
+ stoppedAt: state.stoppedAt ?? null,
151
+ httpRequestCount: state.httpOrder.length,
152
+ realtimeConnectionCount: state.realtimeOrder.length,
153
+ http: {
154
+ totalRecorded: state.httpTotalRecorded,
155
+ evictedCount: state.httpEvictedCount,
156
+ truncated: state.httpTruncated,
157
+ capacity: HTTP_BUFFER_CAPACITY
158
+ },
159
+ realtime: {
160
+ totalRecorded: state.realtimeTotalRecorded,
161
+ evictedCount: state.realtimeEvictedCount,
162
+ truncated: state.realtimeTruncated,
163
+ capacity: REALTIME_BUFFER_CAPACITY
164
+ },
165
+ generation: state.generation,
166
+ enabledInspectors: state.recordingMetadata.enabledInspectors
167
+ }
168
+ });
169
+ const ensureHttpRecord = (requestId, fallback) => {
170
+ const existing = state.httpRecords.get(requestId);
171
+ if (existing) {
172
+ return existing;
173
+ }
174
+ const record = {
175
+ requestId,
176
+ request: fallback?.request || {
177
+ url: "",
178
+ method: "GET",
179
+ headers: {}
180
+ },
181
+ resourceType: fallback?.resourceType || "Other",
182
+ initiator: fallback?.initiator || { type: "other" },
183
+ startTimeMs: fallback?.startTimeMs ?? Date.now(),
184
+ status: fallback?.status || "pending"
185
+ };
186
+ state.httpRecords.set(requestId, record);
187
+ state.httpOrder.push(requestId);
188
+ state.httpTotalRecorded += 1;
189
+ const evicted = trimMap(
190
+ state.httpOrder,
191
+ state.httpRecords,
192
+ HTTP_BUFFER_CAPACITY
193
+ );
194
+ if (evicted > 0) {
195
+ state.httpEvictedCount += evicted;
196
+ state.httpTruncated = true;
197
+ }
198
+ return record;
199
+ };
200
+ const ensureRealtimeRecord = (requestId, createRecord) => {
201
+ const existing = state.realtimeRecords.get(requestId);
202
+ if (existing) {
203
+ return existing;
204
+ }
205
+ const record = createRecord();
206
+ state.realtimeRecords.set(requestId, record);
207
+ state.realtimeOrder.push(requestId);
208
+ state.realtimeTotalRecorded += 1;
209
+ const evicted = trimMap(
210
+ state.realtimeOrder,
211
+ state.realtimeRecords,
212
+ REALTIME_BUFFER_CAPACITY
213
+ );
214
+ if (evicted > 0) {
215
+ state.realtimeEvictedCount += evicted;
216
+ state.realtimeTruncated = true;
217
+ }
218
+ return record;
219
+ };
220
+ const nextMessageId = (prefix) => {
221
+ const id = `${prefix}-${state.nextMessageId}`;
222
+ state.nextMessageId += 1;
223
+ return id;
224
+ };
225
+ return {
226
+ startRecording(metadata) {
227
+ state.isRecording = true;
228
+ state.startedAt = Date.now();
229
+ state.stoppedAt = void 0;
230
+ state.generation += 1;
231
+ state.httpOrder = [];
232
+ state.httpRecords.clear();
233
+ state.httpTotalRecorded = 0;
234
+ state.httpEvictedCount = 0;
235
+ state.httpTruncated = false;
236
+ state.realtimeOrder = [];
237
+ state.realtimeRecords.clear();
238
+ state.realtimeTotalRecorded = 0;
239
+ state.realtimeEvictedCount = 0;
240
+ state.realtimeTruncated = false;
241
+ state.recordingMetadata = {
242
+ enabledInspectors: {
243
+ ...state.recordingMetadata.enabledInspectors,
244
+ ...metadata?.enabledInspectors
245
+ }
246
+ };
247
+ return getStatus();
248
+ },
249
+ stopRecording() {
250
+ if (!state.isRecording) {
251
+ throw new Error("No active network recording for this plugin session");
252
+ }
253
+ state.isRecording = false;
254
+ state.stoppedAt = Date.now();
255
+ return getStatus();
256
+ },
257
+ getStatus,
258
+ getHttpRecord(requestId) {
259
+ return state.httpRecords.get(requestId) || null;
260
+ },
261
+ getRealtimeRecord(requestId) {
262
+ return state.realtimeRecords.get(requestId) || null;
263
+ },
264
+ listRequests(input) {
265
+ const limit = getLimit(input.limit);
266
+ const rows = state.httpOrder.map((requestId) => state.httpRecords.get(requestId)).filter((record) => !!record).reverse().map(createHttpSummary);
267
+ return {
268
+ ...getStatus(),
269
+ ...paginate(rows, `http-${state.generation}`, limit, input.cursor)
270
+ };
271
+ },
272
+ listRealtimeConnections(input) {
273
+ const limit = getLimit(input.limit);
274
+ const rows = state.realtimeOrder.map((requestId) => state.realtimeRecords.get(requestId)).filter((record) => !!record).reverse().map(getRealtimeSummary);
275
+ return {
276
+ ...getStatus(),
277
+ ...paginate(rows, `realtime-${state.generation}`, limit, input.cursor)
278
+ };
279
+ },
280
+ getRequestDetails(requestId) {
281
+ const record = state.httpRecords.get(requestId);
282
+ if (!record) {
283
+ throw new Error(`Unknown request "${requestId}"`);
284
+ }
285
+ return {
286
+ ...getStatus(),
287
+ request: {
288
+ requestId: record.requestId,
289
+ method: record.request.method,
290
+ url: record.request.url,
291
+ type: record.resourceType,
292
+ initiator: record.initiator,
293
+ startTimeMs: record.startTimeMs,
294
+ endTimeMs: record.endTimeMs ?? null,
295
+ durationMs: record.durationMs ?? null,
296
+ request: record.request,
297
+ response: record.response ?? null,
298
+ loadingFinished: record.status === "finished",
299
+ loadingFailed: record.status === "failed",
300
+ failureText: record.error ?? null,
301
+ canceled: record.canceled ?? false,
302
+ progress: record.progress ? {
303
+ loaded: record.progress.loaded,
304
+ total: record.progress.total,
305
+ lengthComputable: record.progress.lengthComputable
306
+ } : null,
307
+ ttfb: record.ttfb ?? null,
308
+ size: record.size ?? null
309
+ }
310
+ };
311
+ },
312
+ getRealtimeConnectionDetails(requestId) {
313
+ const record = state.realtimeRecords.get(requestId);
314
+ if (!record) {
315
+ throw new Error(`Unknown realtime connection "${requestId}"`);
316
+ }
317
+ return {
318
+ ...getStatus(),
319
+ connection: record.kind === "websocket" ? {
320
+ requestId: record.requestId,
321
+ kind: record.kind,
322
+ url: record.url,
323
+ socketId: record.socketId,
324
+ status: record.status,
325
+ startedAt: record.startedAt,
326
+ endedAt: record.endedAt ?? null,
327
+ durationMs: record.durationMs ?? null,
328
+ protocols: record.protocols ?? null,
329
+ options: record.options ?? [],
330
+ error: record.error ?? null,
331
+ closeCode: record.closeCode ?? null,
332
+ closeReason: record.closeReason ?? null,
333
+ messages: record.messages
334
+ } : {
335
+ requestId: record.requestId,
336
+ kind: record.kind,
337
+ status: record.status,
338
+ startedAt: record.startedAt,
339
+ endedAt: record.endedAt ?? null,
340
+ durationMs: record.durationMs ?? null,
341
+ request: record.request ?? null,
342
+ response: record.response ?? null,
343
+ initiator: record.initiator ?? null,
344
+ resourceType: record.resourceType ?? null,
345
+ error: record.error ?? null,
346
+ messages: record.messages
347
+ }
348
+ };
349
+ },
350
+ getRequestBody(requestId) {
351
+ const record = state.httpRecords.get(requestId);
352
+ if (!record) {
353
+ throw new Error(`Unknown request "${requestId}"`);
354
+ }
355
+ return serializeRequestBody(requestId, record.request.postData);
356
+ },
357
+ onRequestSent(event) {
358
+ if (!state.isRecording) {
359
+ return;
360
+ }
361
+ const record = ensureHttpRecord(event.requestId, {
362
+ request: event.request,
363
+ resourceType: event.type,
364
+ initiator: event.initiator,
365
+ startTimeMs: event.timestamp,
366
+ status: "pending"
367
+ });
368
+ record.request = event.request;
369
+ record.resourceType = event.type;
370
+ record.initiator = event.initiator;
371
+ record.startTimeMs = event.timestamp;
372
+ record.status = "pending";
373
+ record.response = void 0;
374
+ record.endTimeMs = void 0;
375
+ record.durationMs = void 0;
376
+ record.size = void 0;
377
+ record.ttfb = void 0;
378
+ record.error = void 0;
379
+ record.canceled = void 0;
380
+ record.progress = void 0;
381
+ },
382
+ onRequestProgress(event) {
383
+ if (!state.isRecording) {
384
+ return;
385
+ }
386
+ const record = ensureHttpRecord(event.requestId);
387
+ record.progress = event;
388
+ record.status = "loading";
389
+ },
390
+ onResponseReceived(event) {
391
+ if (!state.isRecording) {
392
+ return;
393
+ }
394
+ const record = ensureHttpRecord(event.requestId);
395
+ record.response = event.response;
396
+ record.status = "loading";
397
+ },
398
+ onRequestCompleted(event) {
399
+ if (!state.isRecording) {
400
+ return;
401
+ }
402
+ const record = ensureHttpRecord(event.requestId);
403
+ record.status = "finished";
404
+ record.endTimeMs = event.timestamp;
405
+ record.durationMs = event.duration;
406
+ record.size = event.size;
407
+ record.ttfb = event.ttfb;
408
+ },
409
+ onRequestFailed(event) {
410
+ if (!state.isRecording) {
411
+ return;
412
+ }
413
+ const record = ensureHttpRecord(event.requestId);
414
+ record.status = "failed";
415
+ record.endTimeMs = event.timestamp;
416
+ record.error = event.error;
417
+ record.canceled = event.canceled;
418
+ },
419
+ onWebSocketConnect(event) {
420
+ if (!state.isRecording) {
421
+ return;
422
+ }
423
+ ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
424
+ requestId: `ws-${event.socketId}`,
425
+ kind: "websocket",
426
+ url: event.url,
427
+ socketId: event.socketId,
428
+ status: "connecting",
429
+ startedAt: event.timestamp,
430
+ protocols: event.protocols,
431
+ options: event.options,
432
+ messages: []
433
+ }));
434
+ },
435
+ onWebSocketOpen(event) {
436
+ if (!state.isRecording) {
437
+ return;
438
+ }
439
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
440
+ requestId: `ws-${event.socketId}`,
441
+ kind: "websocket",
442
+ url: event.url,
443
+ socketId: event.socketId,
444
+ status: "connecting",
445
+ startedAt: event.timestamp,
446
+ messages: []
447
+ }));
448
+ record.status = "open";
449
+ },
450
+ onWebSocketClose(event) {
451
+ if (!state.isRecording) {
452
+ return;
453
+ }
454
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
455
+ requestId: `ws-${event.socketId}`,
456
+ kind: "websocket",
457
+ url: event.url,
458
+ socketId: event.socketId,
459
+ status: "connecting",
460
+ startedAt: event.timestamp,
461
+ messages: []
462
+ }));
463
+ record.status = "closed";
464
+ record.endedAt = event.timestamp;
465
+ record.durationMs = event.timestamp - record.startedAt;
466
+ record.closeCode = event.code;
467
+ record.closeReason = event.reason;
468
+ },
469
+ onWebSocketMessageSent(event) {
470
+ if (!state.isRecording) {
471
+ return;
472
+ }
473
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
474
+ requestId: `ws-${event.socketId}`,
475
+ kind: "websocket",
476
+ url: event.url,
477
+ socketId: event.socketId,
478
+ status: "connecting",
479
+ startedAt: event.timestamp,
480
+ messages: []
481
+ }));
482
+ const message = {
483
+ id: nextMessageId(record.requestId),
484
+ direction: "sent",
485
+ data: event.data,
486
+ messageType: event.messageType,
487
+ timestamp: event.timestamp
488
+ };
489
+ record.messages = [...record.messages, message].slice(
490
+ -32
491
+ );
492
+ },
493
+ onWebSocketMessageReceived(event) {
494
+ if (!state.isRecording) {
495
+ return;
496
+ }
497
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
498
+ requestId: `ws-${event.socketId}`,
499
+ kind: "websocket",
500
+ url: event.url,
501
+ socketId: event.socketId,
502
+ status: "connecting",
503
+ startedAt: event.timestamp,
504
+ messages: []
505
+ }));
506
+ const message = {
507
+ id: nextMessageId(record.requestId),
508
+ direction: "received",
509
+ data: event.data,
510
+ messageType: event.messageType,
511
+ timestamp: event.timestamp
512
+ };
513
+ record.messages = [...record.messages, message].slice(
514
+ -32
515
+ );
516
+ },
517
+ onWebSocketError(event) {
518
+ if (!state.isRecording) {
519
+ return;
520
+ }
521
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
522
+ requestId: `ws-${event.socketId}`,
523
+ kind: "websocket",
524
+ url: event.url,
525
+ socketId: event.socketId,
526
+ status: "connecting",
527
+ startedAt: event.timestamp,
528
+ messages: []
529
+ }));
530
+ record.status = "error";
531
+ record.error = event.error;
532
+ },
533
+ onWebSocketConnectionStatusChanged(event) {
534
+ if (!state.isRecording) {
535
+ return;
536
+ }
537
+ const record = ensureRealtimeRecord(`ws-${event.socketId}`, () => ({
538
+ requestId: `ws-${event.socketId}`,
539
+ kind: "websocket",
540
+ url: event.url,
541
+ socketId: event.socketId,
542
+ status: "connecting",
543
+ startedAt: event.timestamp,
544
+ messages: []
545
+ }));
546
+ record.status = event.status;
547
+ },
548
+ onSSEOpen(event) {
549
+ if (!state.isRecording) {
550
+ return;
551
+ }
552
+ const httpRecord = state.httpRecords.get(event.requestId);
553
+ ensureRealtimeRecord(event.requestId, () => ({
554
+ requestId: event.requestId,
555
+ kind: "sse",
556
+ status: "open",
557
+ startedAt: httpRecord?.startTimeMs ?? event.timestamp,
558
+ request: httpRecord?.request,
559
+ response: event.response,
560
+ initiator: httpRecord?.initiator,
561
+ resourceType: httpRecord?.resourceType,
562
+ messages: []
563
+ }));
564
+ },
565
+ onSSEMessage(event) {
566
+ if (!state.isRecording) {
567
+ return;
568
+ }
569
+ const record = ensureRealtimeRecord(event.requestId, () => ({
570
+ requestId: event.requestId,
571
+ kind: "sse",
572
+ status: "connecting",
573
+ startedAt: event.timestamp,
574
+ messages: []
575
+ }));
576
+ record.messages = [
577
+ ...record.messages,
578
+ {
579
+ id: nextMessageId(record.requestId),
580
+ type: event.payload.type,
581
+ data: event.payload.data,
582
+ timestamp: event.timestamp
583
+ }
584
+ ].slice(-32);
585
+ },
586
+ onSSEError(event) {
587
+ if (!state.isRecording) {
588
+ return;
589
+ }
590
+ const record = ensureRealtimeRecord(event.requestId, () => ({
591
+ requestId: event.requestId,
592
+ kind: "sse",
593
+ status: "connecting",
594
+ startedAt: event.timestamp,
595
+ messages: []
596
+ }));
597
+ record.status = "error";
598
+ record.error = event.error.message;
599
+ },
600
+ onSSEClose(event) {
601
+ if (!state.isRecording) {
602
+ return;
603
+ }
604
+ const record = ensureRealtimeRecord(event.requestId, () => ({
605
+ requestId: event.requestId,
606
+ kind: "sse",
607
+ status: "connecting",
608
+ startedAt: event.timestamp,
609
+ messages: []
610
+ }));
611
+ record.status = "closed";
612
+ record.endedAt = event.timestamp;
613
+ record.durationMs = event.timestamp - record.startedAt;
614
+ }
615
+ };
616
+ };
617
+ const getNetworkActivityAgentState = /* @__PURE__ */ (() => {
618
+ let instance = null;
619
+ return () => {
620
+ if (!instance) {
621
+ instance = createNetworkActivityAgentState();
622
+ }
623
+ return instance;
624
+ };
625
+ })();
626
+ const startRecordingTool = {
627
+ name: "startRecording",
628
+ description: "Start recording network activity in the fallback network activity plugin.",
629
+ inputSchema: {
630
+ type: "object",
631
+ properties: {}
632
+ }
633
+ };
634
+ const stopRecordingTool = {
635
+ name: "stopRecording",
636
+ description: "Stop recording network activity without clearing the captured plugin buffer.",
637
+ inputSchema: {
638
+ type: "object",
639
+ properties: {}
640
+ }
641
+ };
642
+ const getRecordingStatusTool = {
643
+ name: "getRecordingStatus",
644
+ description: "Return network activity plugin recording state and buffer metadata.",
645
+ inputSchema: {
646
+ type: "object",
647
+ properties: {}
648
+ }
649
+ };
650
+ const listRequestsTool = {
651
+ name: "listRequests",
652
+ description: "List captured HTTP request summaries with cursor pagination from the fallback plugin.",
653
+ inputSchema: {
654
+ type: "object",
655
+ properties: {
656
+ limit: {
657
+ type: "number",
658
+ description: "Maximum number of requests to return. Defaults to 20."
659
+ },
660
+ cursor: {
661
+ type: "string",
662
+ description: "Opaque pagination cursor from a previous listRequests call."
663
+ }
664
+ }
665
+ }
666
+ };
667
+ const getRequestDetailsTool = {
668
+ name: "getRequestDetails",
669
+ description: "Return detailed metadata for a captured HTTP request without fetching response body.",
670
+ inputSchema: {
671
+ type: "object",
672
+ properties: {
673
+ requestId: {
674
+ type: "string",
675
+ description: "Captured plugin request ID to inspect."
676
+ }
677
+ },
678
+ required: ["requestId"]
679
+ }
680
+ };
681
+ const getRequestBodyTool = {
682
+ name: "getRequestBody",
683
+ description: "Return the captured request body for a plugin-recorded HTTP request when available.",
684
+ inputSchema: {
685
+ type: "object",
686
+ properties: {
687
+ requestId: {
688
+ type: "string",
689
+ description: "Captured plugin request ID to inspect."
690
+ }
691
+ },
692
+ required: ["requestId"]
693
+ }
694
+ };
695
+ const getResponseBodyTool = {
696
+ name: "getResponseBody",
697
+ description: "Return the captured response body for a plugin-recorded HTTP request when available.",
698
+ inputSchema: {
699
+ type: "object",
700
+ properties: {
701
+ requestId: {
702
+ type: "string",
703
+ description: "Captured plugin request ID to inspect."
704
+ }
705
+ },
706
+ required: ["requestId"]
707
+ }
708
+ };
709
+ const listRealtimeConnectionsTool = {
710
+ name: "listRealtimeConnections",
711
+ description: "List captured WebSocket and SSE connections with cursor pagination.",
712
+ inputSchema: {
713
+ type: "object",
714
+ properties: {
715
+ limit: {
716
+ type: "number",
717
+ description: "Maximum number of realtime connections to return. Defaults to 20."
718
+ },
719
+ cursor: {
720
+ type: "string",
721
+ description: "Opaque pagination cursor from a previous listRealtimeConnections call."
722
+ }
723
+ }
724
+ }
725
+ };
726
+ const getRealtimeConnectionDetailsTool = {
727
+ name: "getRealtimeConnectionDetails",
728
+ description: "Return details for a captured WebSocket or SSE connection, including recent messages.",
729
+ inputSchema: {
730
+ type: "object",
731
+ properties: {
732
+ requestId: {
733
+ type: "string",
734
+ description: "Captured realtime request ID to inspect."
735
+ }
736
+ },
737
+ required: ["requestId"]
738
+ }
739
+ };
740
+ const pluginId = "@rozenite/network-activity-plugin";
741
+ const useNetworkActivityAgentTools = ({
742
+ client,
743
+ networkInspector,
744
+ enabledInspectors
745
+ }) => {
746
+ const state = getNetworkActivityAgentState();
747
+ react.useEffect(() => {
748
+ const unsubscribe = [
749
+ networkInspector.http.on(
750
+ "request-sent",
751
+ (event) => state.onRequestSent(event)
752
+ ),
753
+ networkInspector.http.on(
754
+ "request-progress",
755
+ (event) => state.onRequestProgress(event)
756
+ ),
757
+ networkInspector.http.on(
758
+ "response-received",
759
+ (event) => state.onResponseReceived(event)
760
+ ),
761
+ networkInspector.http.on(
762
+ "request-completed",
763
+ (event) => state.onRequestCompleted(event)
764
+ ),
765
+ networkInspector.http.on(
766
+ "request-failed",
767
+ (event) => state.onRequestFailed(event)
768
+ ),
769
+ networkInspector.websocket.on(
770
+ "websocket-connect",
771
+ (event) => state.onWebSocketConnect(event)
772
+ ),
773
+ networkInspector.websocket.on(
774
+ "websocket-open",
775
+ (event) => state.onWebSocketOpen(event)
776
+ ),
777
+ networkInspector.websocket.on(
778
+ "websocket-close",
779
+ (event) => state.onWebSocketClose(event)
780
+ ),
781
+ networkInspector.websocket.on(
782
+ "websocket-message-sent",
783
+ (event) => state.onWebSocketMessageSent(event)
784
+ ),
785
+ networkInspector.websocket.on(
786
+ "websocket-message-received",
787
+ (event) => state.onWebSocketMessageReceived(event)
788
+ ),
789
+ networkInspector.websocket.on(
790
+ "websocket-error",
791
+ (event) => state.onWebSocketError(event)
792
+ ),
793
+ networkInspector.websocket.on(
794
+ "websocket-connection-status-changed",
795
+ (event) => state.onWebSocketConnectionStatusChanged(event)
796
+ ),
797
+ networkInspector.sse.on("sse-open", (event) => state.onSSEOpen(event)),
798
+ networkInspector.sse.on("sse-message", (event) => state.onSSEMessage(event)),
799
+ networkInspector.sse.on("sse-error", (event) => state.onSSEError(event)),
800
+ networkInspector.sse.on("sse-close", (event) => state.onSSEClose(event))
801
+ ];
802
+ return () => {
803
+ unsubscribe.forEach((remove) => remove());
804
+ };
805
+ }, [networkInspector, state]);
806
+ react.useEffect(() => {
807
+ if (!client) {
808
+ return;
809
+ }
810
+ const subscriptions = [
811
+ client.onMessage("network-enable", () => {
812
+ state.startRecording({ enabledInspectors });
813
+ }),
814
+ client.onMessage("network-disable", () => {
815
+ if (state.getStatus().recording.isRecording) {
816
+ state.stopRecording();
817
+ }
818
+ })
819
+ ];
820
+ return () => {
821
+ subscriptions.forEach((subscription) => subscription.remove());
822
+ };
823
+ }, [client, enabledInspectors, state]);
824
+ agentBridge.useRozenitePluginAgentTool({
825
+ pluginId,
826
+ tool: startRecordingTool,
827
+ handler: () => {
828
+ networkInspector.http.getNetworkRequestsRegistry().clear();
829
+ const result = state.startRecording({ enabledInspectors });
830
+ networkInspector.enable(enabledInspectors);
831
+ return {
832
+ started: true,
833
+ ...result
834
+ };
835
+ }
836
+ });
837
+ agentBridge.useRozenitePluginAgentTool({
838
+ pluginId,
839
+ tool: stopRecordingTool,
840
+ handler: () => {
841
+ const result = state.stopRecording();
842
+ networkInspector.disable();
843
+ return {
844
+ stopped: true,
845
+ ...result
846
+ };
847
+ }
848
+ });
849
+ agentBridge.useRozenitePluginAgentTool({
850
+ pluginId,
851
+ tool: getRecordingStatusTool,
852
+ handler: () => state.getStatus()
853
+ });
854
+ agentBridge.useRozenitePluginAgentTool({
855
+ pluginId,
856
+ tool: listRequestsTool,
857
+ handler: (input = {}) => state.listRequests(input)
858
+ });
859
+ agentBridge.useRozenitePluginAgentTool({
860
+ pluginId,
861
+ tool: getRequestDetailsTool,
862
+ handler: ({ requestId }) => state.getRequestDetails(requestId)
863
+ });
864
+ agentBridge.useRozenitePluginAgentTool({
865
+ pluginId,
866
+ tool: getRequestBodyTool,
867
+ handler: ({ requestId }) => state.getRequestBody(requestId)
868
+ });
869
+ agentBridge.useRozenitePluginAgentTool({
870
+ pluginId,
871
+ tool: getResponseBodyTool,
872
+ handler: async ({ requestId }) => {
873
+ const record = state.getHttpRecord(requestId);
874
+ if (!record) {
875
+ throw new Error(`Unknown request "${requestId}"`);
876
+ }
877
+ if (record.status === "failed") {
878
+ return {
879
+ requestId,
880
+ available: false,
881
+ reason: "Response body is unavailable because the request failed."
882
+ };
883
+ }
884
+ if (record.status !== "finished") {
885
+ return {
886
+ requestId,
887
+ available: false,
888
+ reason: "Response body is unavailable until the request finishes loading."
889
+ };
890
+ }
891
+ const request = networkInspector.http.getNetworkRequestsRegistry().getEntry(requestId);
892
+ if (!request) {
893
+ return {
894
+ requestId,
895
+ available: false,
896
+ reason: "Response body is unavailable because the request object is no longer in the plugin registry."
897
+ };
898
+ }
899
+ const body = await bootRecording.getResponseBody(request);
900
+ if (body === null) {
901
+ return {
902
+ requestId,
903
+ available: false,
904
+ reason: "The plugin could not extract a text response body for this request."
905
+ };
906
+ }
907
+ return {
908
+ requestId,
909
+ available: true,
910
+ body,
911
+ base64Encoded: false,
912
+ decoded: false,
913
+ mimeType: record.response?.contentType
914
+ };
915
+ }
916
+ });
917
+ agentBridge.useRozenitePluginAgentTool({
918
+ pluginId,
919
+ tool: listRealtimeConnectionsTool,
920
+ handler: (input = {}) => state.listRealtimeConnections(input)
921
+ });
922
+ agentBridge.useRozenitePluginAgentTool({
923
+ pluginId,
924
+ tool: getRealtimeConnectionDetailsTool,
925
+ handler: ({ requestId }) => state.getRealtimeConnectionDetails(requestId)
926
+ });
927
+ };
928
+ const overridesRegistry = bootRecording.getOverridesRegistry();
929
+ const useHttpInspector = (client, httpInspector, isEnabled, isRecordingEnabled) => {
930
+ react.useEffect(() => {
931
+ if (!client || !isEnabled) {
932
+ return;
933
+ }
934
+ const networkRequestsRegistry = httpInspector.getNetworkRequestsRegistry();
935
+ const subscriptions = [
936
+ client.onMessage("network-enable", () => {
937
+ httpInspector.enable();
938
+ }),
939
+ client.onMessage("network-disable", () => {
940
+ httpInspector.disable();
941
+ }),
942
+ client.onMessage("set-overrides", (data) => {
943
+ overridesRegistry.setOverrides(data.overrides);
944
+ }),
945
+ client.onMessage("get-response-body", async ({ requestId }) => {
946
+ const request = networkRequestsRegistry.getEntry(requestId);
947
+ if (!request) {
948
+ return;
949
+ }
950
+ const body = await bootRecording.getResponseBody(request);
951
+ client.send("response-body", {
952
+ requestId,
953
+ body
954
+ });
955
+ })
956
+ ];
957
+ if (isRecordingEnabled) {
958
+ httpInspector.enable();
959
+ }
960
+ return () => {
961
+ subscriptions.forEach((subscription) => subscription.remove());
962
+ httpInspector.dispose();
963
+ };
964
+ }, [client, httpInspector, isEnabled, isRecordingEnabled]);
965
+ };
966
+ const useWebSocketInspector = (client, websocketInspector, isEnabled, isRecordingEnabled) => {
967
+ react.useEffect(() => {
968
+ if (!client || !isEnabled) {
969
+ return;
970
+ }
971
+ const subscriptions = [
972
+ client.onMessage("network-enable", () => {
973
+ websocketInspector.enable();
974
+ }),
975
+ client.onMessage("network-disable", () => {
976
+ websocketInspector.disable();
977
+ })
978
+ ];
979
+ if (isRecordingEnabled) {
980
+ websocketInspector.enable();
981
+ }
982
+ return () => {
983
+ subscriptions.forEach((subscription) => subscription.remove());
984
+ websocketInspector.dispose();
985
+ };
986
+ }, [client, websocketInspector, isEnabled, isRecordingEnabled]);
987
+ };
988
+ const useSSEInspector = (client, sseInspector, isEnabled, isRecordingEnabled) => {
989
+ react.useEffect(() => {
990
+ if (!client || !isEnabled) {
991
+ return;
992
+ }
993
+ const subscriptions = [
994
+ client.onMessage("network-enable", () => {
995
+ sseInspector.enable();
996
+ }),
997
+ client.onMessage("network-disable", () => {
998
+ sseInspector.disable();
999
+ })
1000
+ ];
1001
+ if (isRecordingEnabled) {
1002
+ sseInspector.enable();
1003
+ }
1004
+ return () => {
1005
+ subscriptions.forEach((subscription) => subscription.remove());
1006
+ sseInspector.dispose();
1007
+ };
1008
+ }, [client, sseInspector, isEnabled, isRecordingEnabled]);
1009
+ };
1010
+ const inspectorsConfig = bootRecording.createNetworkInspectorsConfiguration();
1011
+ const useNetworkActivityDevTools = (config = bootRecording.DEFAULT_CONFIG) => {
1012
+ const isRecordingEnabledRef = react.useRef(false);
1013
+ const client = pluginBridge.useRozeniteDevToolsClient({
1014
+ pluginId: "@rozenite/network-activity-plugin"
1015
+ });
1016
+ const isHttpInspectorEnabled = config.inspectors?.http ?? true;
1017
+ const isWebSocketInspectorEnabled = config.inspectors?.websocket ?? true;
1018
+ const isSSEInspectorEnabled = config.inspectors?.sse ?? true;
1019
+ const showUrlAsName = config.clientUISettings?.showUrlAsName;
1020
+ const { eventsListener, networkInspector } = inspectorsConfig;
1021
+ useNetworkActivityAgentTools({
1022
+ client,
1023
+ networkInspector,
1024
+ enabledInspectors: {
1025
+ http: isHttpInspectorEnabled,
1026
+ websocket: isWebSocketInspectorEnabled,
1027
+ sse: isSSEInspectorEnabled
1028
+ }
1029
+ });
1030
+ react.useEffect(() => {
1031
+ if (!client) {
1032
+ return;
1033
+ }
1034
+ bootRecording.validateConfig(config);
1035
+ }, [config]);
1036
+ react.useEffect(() => {
1037
+ if (!client) {
1038
+ return;
1039
+ }
1040
+ const sendClientUISettings = () => {
1041
+ client.send("client-ui-settings", {
1042
+ settings: {
1043
+ showUrlAsName: showUrlAsName ?? bootRecording.DEFAULT_CONFIG.clientUISettings?.showUrlAsName
1044
+ }
1045
+ });
1046
+ };
1047
+ const subscriptions = [
1048
+ client.onMessage("network-enable", () => {
1049
+ isRecordingEnabledRef.current = true;
1050
+ eventsListener.connect(client.send, (message) => {
1051
+ const type = message.type;
1052
+ if (bootRecording.isHttpEvent(type)) {
1053
+ return isHttpInspectorEnabled;
1054
+ }
1055
+ if (bootRecording.isWebSocketEvent(type)) {
1056
+ return isWebSocketInspectorEnabled;
1057
+ }
1058
+ if (bootRecording.isSSEEvent(type)) {
1059
+ return isSSEInspectorEnabled;
1060
+ }
1061
+ return true;
1062
+ });
1063
+ }),
1064
+ client.onMessage("network-disable", () => {
1065
+ isRecordingEnabledRef.current = false;
1066
+ }),
1067
+ client.onMessage("get-client-ui-settings", () => {
1068
+ sendClientUISettings();
1069
+ })
1070
+ ];
1071
+ sendClientUISettings();
1072
+ return () => {
1073
+ subscriptions.forEach((subscription) => subscription.remove());
1074
+ };
1075
+ }, [
1076
+ client,
1077
+ showUrlAsName,
1078
+ isHttpInspectorEnabled,
1079
+ isWebSocketInspectorEnabled,
1080
+ isSSEInspectorEnabled
1081
+ ]);
1082
+ useHttpInspector(
1083
+ client,
1084
+ networkInspector.http,
1085
+ isHttpInspectorEnabled,
1086
+ isRecordingEnabledRef.current
1087
+ );
1088
+ useWebSocketInspector(
1089
+ client,
1090
+ networkInspector.websocket,
1091
+ isWebSocketInspectorEnabled,
1092
+ isRecordingEnabledRef.current
1093
+ );
1094
+ useSSEInspector(
1095
+ client,
1096
+ networkInspector.sse,
1097
+ isSSEInspectorEnabled,
1098
+ isRecordingEnabledRef.current
1099
+ );
1100
+ return client;
1101
+ };
1102
+ exports.useNetworkActivityDevTools = useNetworkActivityDevTools;