@rozenite/network-activity-plugin 1.0.0-alpha.8 → 1.0.0-alpha.9

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 (87) hide show
  1. package/dist/App.html +2 -2
  2. package/dist/assets/{App-lNMijPJ4.js → App-CA1Fbh0I.js} +11995 -10804
  3. package/dist/assets/{App-R2ZMH9wJ.css → App-DoHQsY5s.css} +46 -0
  4. package/dist/event-source.cjs +22 -0
  5. package/dist/event-source.js +23 -0
  6. package/dist/rozenite.json +1 -1
  7. package/dist/src/react-native/{network-inspector.d.ts → http/network-inspector.d.ts} +1 -1
  8. package/dist/src/react-native/sse/event-source.d.ts +2 -0
  9. package/dist/src/react-native/sse/sse-inspector.d.ts +9 -0
  10. package/dist/src/react-native/sse/sse-interceptor.d.ts +36 -0
  11. package/dist/src/react-native/sse/types.d.ts +6 -0
  12. package/dist/src/react-native/utils.d.ts +6 -0
  13. package/dist/src/react-native/websocket/websocket-inspector.d.ts +9 -0
  14. package/dist/src/react-native/websocket/websocket-interceptor.d.ts +74 -0
  15. package/dist/src/shared/client.d.ts +5 -2
  16. package/dist/src/shared/sse-events.d.ts +35 -0
  17. package/dist/src/shared/websocket-events.d.ts +60 -0
  18. package/dist/src/ui/components/Badge.d.ts +1 -1
  19. package/dist/src/ui/components/Button.d.ts +1 -1
  20. package/dist/src/ui/components/JsonTreeCopyableItem.d.ts +7 -0
  21. package/dist/src/ui/components/RequestList.d.ts +6 -26
  22. package/dist/src/ui/components/SidePanel.d.ts +1 -0
  23. package/dist/src/ui/components/Toolbar.d.ts +1 -0
  24. package/dist/src/ui/hooks/useCopyToClipboard.d.ts +4 -0
  25. package/dist/src/ui/state/derived.d.ts +5 -0
  26. package/dist/src/ui/state/hooks.d.ts +17 -0
  27. package/dist/src/ui/state/model.d.ts +98 -0
  28. package/dist/src/ui/state/store.d.ts +24 -0
  29. package/dist/src/ui/tabs/CookiesTab.d.ts +3 -6
  30. package/dist/src/ui/tabs/HeadersTab.d.ts +3 -15
  31. package/dist/src/ui/tabs/MessagesTab.d.ts +5 -0
  32. package/dist/src/ui/tabs/RequestTab.d.ts +2 -7
  33. package/dist/src/ui/tabs/ResponseTab.d.ts +2 -8
  34. package/dist/src/ui/tabs/SSEMessagesTab.d.ts +5 -0
  35. package/dist/src/ui/tabs/TimingTab.d.ts +3 -5
  36. package/dist/src/ui/types.d.ts +4 -1
  37. package/dist/src/ui/utils/assert.d.ts +1 -0
  38. package/dist/src/ui/utils/copyToClipboard.d.ts +1 -0
  39. package/dist/src/ui/utils/getId.d.ts +1 -0
  40. package/dist/src/ui/utils/getStatusColor.d.ts +1 -0
  41. package/dist/useNetworkActivityDevTools.cjs +423 -34
  42. package/dist/useNetworkActivityDevTools.js +421 -34
  43. package/package.json +19 -8
  44. package/src/react-native/{network-inspector.ts → http/network-inspector.ts} +13 -34
  45. package/src/react-native/{xml-request.d.ts → http/xml-request.d.ts} +1 -0
  46. package/src/react-native/sse/event-source.ts +25 -0
  47. package/src/react-native/sse/sse-inspector.ts +117 -0
  48. package/src/react-native/sse/sse-interceptor.ts +162 -0
  49. package/src/react-native/sse/types.ts +9 -0
  50. package/src/react-native/useNetworkActivityDevTools.ts +75 -1
  51. package/src/react-native/utils.ts +43 -0
  52. package/src/react-native/websocket/websocket-inspector.ts +180 -0
  53. package/src/react-native/websocket/websocket-interceptor.d.ts +4 -0
  54. package/src/react-native/websocket/websocket-interceptor.ts +166 -0
  55. package/src/shared/client.ts +6 -2
  56. package/src/shared/sse-events.ts +44 -0
  57. package/src/shared/websocket-events.ts +79 -0
  58. package/src/ui/components/JsonTree.tsx +13 -0
  59. package/src/ui/components/JsonTreeCopyableItem.tsx +33 -0
  60. package/src/ui/components/RequestList.tsx +42 -124
  61. package/src/ui/components/SidePanel.tsx +323 -0
  62. package/src/ui/components/Tabs.tsx +1 -1
  63. package/src/ui/components/Toolbar.tsx +45 -0
  64. package/src/ui/hooks/useCopyToClipboard.ts +28 -0
  65. package/src/ui/state/derived.ts +112 -0
  66. package/src/ui/state/hooks.ts +44 -0
  67. package/src/ui/state/model.ts +129 -0
  68. package/src/ui/state/store.ts +559 -0
  69. package/src/ui/tabs/CookiesTab.tsx +162 -176
  70. package/src/ui/tabs/HeadersTab.tsx +23 -30
  71. package/src/ui/tabs/MessagesTab.tsx +276 -0
  72. package/src/ui/tabs/RequestTab.tsx +8 -13
  73. package/src/ui/tabs/ResponseTab.tsx +6 -10
  74. package/src/ui/tabs/SSEMessagesTab.tsx +213 -0
  75. package/src/ui/tabs/TimingTab.tsx +30 -43
  76. package/src/ui/types.ts +4 -1
  77. package/src/ui/utils/assert.ts +5 -0
  78. package/src/ui/utils/copyToClipboard.ts +3 -0
  79. package/src/ui/utils/getId.ts +10 -0
  80. package/src/ui/utils/getStatusColor.ts +15 -0
  81. package/src/ui/views/InspectorView.tsx +24 -320
  82. package/tailwind.config.ts +3 -0
  83. package/vite.config.ts +12 -0
  84. /package/dist/src/react-native/{network-requests-registry.d.ts → http/network-requests-registry.d.ts} +0 -0
  85. /package/dist/src/react-native/{xhr-interceptor.d.ts → http/xhr-interceptor.d.ts} +0 -0
  86. /package/src/react-native/{network-requests-registry.ts → http/network-requests-registry.ts} +0 -0
  87. /package/src/react-native/{xhr-interceptor.ts → http/xhr-interceptor.ts} +0 -0
@@ -0,0 +1,559 @@
1
+ import { createStore } from 'zustand';
2
+ import {
3
+ NetworkActivityDevToolsClient,
4
+ NetworkActivityEventMap,
5
+ RequestId,
6
+ } from '../../shared/client';
7
+ import {
8
+ NetworkEntry,
9
+ HttpNetworkEntry,
10
+ WebSocketNetworkEntry,
11
+ WebSocketMessage,
12
+ SSENetworkEntry,
13
+ SSEMessage,
14
+ } from './model';
15
+ import { getHttpHeaderValue } from '../utils/getHttpHeaderValue';
16
+ import { getId } from '../utils/getId';
17
+ import { assert } from '../utils/assert';
18
+
19
+ const MAX_WEBSOCKET_MESSAGES_PER_CONNECTION = 32;
20
+ const MAX_SSE_MESSAGES_PER_CONNECTION = 32;
21
+
22
+ export interface NetworkActivityState {
23
+ // State
24
+ isRecording: boolean;
25
+ selectedRequestId: RequestId | null;
26
+ networkEntries: Map<RequestId, NetworkEntry>;
27
+ websocketMessages: Map<RequestId, WebSocketMessage[]>;
28
+
29
+ // Internal state (not exposed in interface)
30
+ _unsubscribeFunctions?: Array<{ remove: () => void }>;
31
+ _client?: NetworkActivityDevToolsClient;
32
+
33
+ // Actions
34
+ actions: {
35
+ setRecording: (isRecording: boolean) => void;
36
+ setSelectedRequest: (requestId: RequestId | null) => void;
37
+ clearRequests: () => void;
38
+ };
39
+
40
+ // Event handling
41
+ handleEvent: <K extends keyof NetworkActivityEventMap>(
42
+ eventType: K,
43
+ data: NetworkActivityEventMap[K]
44
+ ) => void;
45
+
46
+ // Client management
47
+ client: {
48
+ setupClient: (client: NetworkActivityDevToolsClient) => void;
49
+ cleanupClient: () => void;
50
+ };
51
+ }
52
+
53
+ export const createNetworkActivityStore = () =>
54
+ createStore<NetworkActivityState>((set, get) => ({
55
+ // Initial state
56
+ isRecording: false,
57
+ selectedRequestId: null,
58
+ networkEntries: new Map(),
59
+ websocketMessages: new Map(),
60
+
61
+ // Actions
62
+ actions: {
63
+ setRecording: (isRecording: boolean) => {
64
+ const { _client } = get();
65
+ assert(!!_client, 'Client is not set');
66
+
67
+ _client.send(isRecording ? 'network-enable' : 'network-disable', {});
68
+ set({ isRecording });
69
+ },
70
+ setSelectedRequest: (requestId: RequestId | null) =>
71
+ set({ selectedRequestId: requestId }),
72
+ clearRequests: () =>
73
+ set({
74
+ networkEntries: new Map(),
75
+ websocketMessages: new Map(),
76
+ selectedRequestId: null,
77
+ }),
78
+ },
79
+ // Event handling
80
+ handleEvent: <K extends keyof NetworkActivityEventMap>(
81
+ eventType: K,
82
+ data: NetworkActivityEventMap[K]
83
+ ) => {
84
+ switch (eventType) {
85
+ case 'request-sent': {
86
+ const eventData = data as NetworkActivityEventMap['request-sent'];
87
+ set((state) => {
88
+ const entry: HttpNetworkEntry = {
89
+ id: eventData.requestId,
90
+ type: 'http',
91
+ timestamp: eventData.timestamp,
92
+ request: {
93
+ url: eventData.request.url,
94
+ method: eventData.request.method,
95
+ headers: eventData.request.headers,
96
+ body: eventData.request.postData
97
+ ? {
98
+ type:
99
+ getHttpHeaderValue(
100
+ eventData.request.headers,
101
+ 'content-type'
102
+ )?.split(';')[0] || 'text/plain',
103
+ data: eventData.request.postData,
104
+ }
105
+ : undefined,
106
+ },
107
+ status: 'pending',
108
+ initiator: eventData.initiator,
109
+ resourceType: eventData.type,
110
+ };
111
+
112
+ const newEntries = new Map(state.networkEntries);
113
+ newEntries.set(eventData.requestId, entry);
114
+ return { networkEntries: newEntries };
115
+ });
116
+ break;
117
+ }
118
+
119
+ case 'response-received': {
120
+ const eventData =
121
+ data as NetworkActivityEventMap['response-received'];
122
+ set((state) => {
123
+ const entry = state.networkEntries.get(eventData.requestId);
124
+ if (!entry || entry.type !== 'http') return state;
125
+
126
+ const httpEntry = entry as HttpNetworkEntry;
127
+ const updatedEntry: HttpNetworkEntry = {
128
+ ...httpEntry,
129
+ status: 'loading',
130
+ response: eventData.response,
131
+ };
132
+
133
+ const newEntries = new Map(state.networkEntries);
134
+ newEntries.set(eventData.requestId, updatedEntry);
135
+ return { networkEntries: newEntries };
136
+ });
137
+ break;
138
+ }
139
+
140
+ case 'request-completed': {
141
+ const eventData =
142
+ data as NetworkActivityEventMap['request-completed'];
143
+ set((state) => {
144
+ const entry = state.networkEntries.get(eventData.requestId);
145
+ if (!entry || entry.type !== 'http') return state;
146
+
147
+ const httpEntry = entry as HttpNetworkEntry;
148
+ const updatedEntry: HttpNetworkEntry = {
149
+ ...httpEntry,
150
+ status: 'finished',
151
+ duration: eventData.duration,
152
+ size: eventData.size,
153
+ ttfb: eventData.ttfb,
154
+ };
155
+
156
+ const newEntries = new Map(state.networkEntries);
157
+ newEntries.set(eventData.requestId, updatedEntry);
158
+ return { networkEntries: newEntries };
159
+ });
160
+ break;
161
+ }
162
+
163
+ case 'request-failed': {
164
+ const eventData = data as NetworkActivityEventMap['request-failed'];
165
+ set((state) => {
166
+ const entry = state.networkEntries.get(eventData.requestId);
167
+ if (!entry || entry.type !== 'http') return state;
168
+
169
+ const httpEntry = entry as HttpNetworkEntry;
170
+ const updatedEntry: HttpNetworkEntry = {
171
+ ...httpEntry,
172
+ status: 'failed',
173
+ error: eventData.error,
174
+ };
175
+
176
+ const newEntries = new Map(state.networkEntries);
177
+ newEntries.set(eventData.requestId, updatedEntry);
178
+ return { networkEntries: newEntries };
179
+ });
180
+ break;
181
+ }
182
+
183
+ case 'response-body': {
184
+ const eventData = data as NetworkActivityEventMap['response-body'];
185
+ set((state) => {
186
+ const entry = state.networkEntries.get(eventData.requestId);
187
+ if (!entry || entry.type !== 'http') return state;
188
+
189
+ const httpEntry = entry as HttpNetworkEntry;
190
+ const updatedEntry: HttpNetworkEntry = {
191
+ ...httpEntry,
192
+ response: httpEntry.response
193
+ ? {
194
+ ...httpEntry.response,
195
+ body: eventData.body
196
+ ? {
197
+ type:
198
+ getHttpHeaderValue(
199
+ httpEntry.response?.headers ?? {},
200
+ 'content-type'
201
+ )?.split(';')[0] || 'text/plain',
202
+ data: eventData.body,
203
+ }
204
+ : undefined,
205
+ }
206
+ : undefined,
207
+ };
208
+
209
+ const newEntries = new Map(state.networkEntries);
210
+ newEntries.set(eventData.requestId, updatedEntry);
211
+ return { networkEntries: newEntries };
212
+ });
213
+ break;
214
+ }
215
+
216
+ case 'websocket-connect': {
217
+ const eventData =
218
+ data as NetworkActivityEventMap['websocket-connect'];
219
+ set((state) => {
220
+ const entry: WebSocketNetworkEntry = {
221
+ id: `ws-${eventData.socketId}`,
222
+ type: 'websocket',
223
+ timestamp: eventData.timestamp,
224
+ connection: {
225
+ url: eventData.url,
226
+ socketId: eventData.socketId,
227
+ protocols: eventData.protocols || undefined,
228
+ options: eventData.options,
229
+ },
230
+ status: 'connecting',
231
+ };
232
+
233
+ const newEntries = new Map(state.networkEntries);
234
+ newEntries.set(entry.id, entry);
235
+
236
+ const newMessages = new Map(state.websocketMessages);
237
+ newMessages.set(entry.id, []);
238
+
239
+ return {
240
+ networkEntries: newEntries,
241
+ websocketMessages: newMessages,
242
+ };
243
+ });
244
+ break;
245
+ }
246
+
247
+ case 'websocket-open': {
248
+ const eventData = data as NetworkActivityEventMap['websocket-open'];
249
+ set((state) => {
250
+ const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
251
+ if (!entry || entry.type !== 'websocket') return state;
252
+
253
+ const wsEntry = entry as WebSocketNetworkEntry;
254
+ const updatedEntry: WebSocketNetworkEntry = {
255
+ ...wsEntry,
256
+ status: 'open',
257
+ };
258
+
259
+ const newEntries = new Map(state.networkEntries);
260
+ newEntries.set(entry.id, updatedEntry);
261
+ return { networkEntries: newEntries };
262
+ });
263
+ break;
264
+ }
265
+
266
+ case 'websocket-close': {
267
+ const eventData = data as NetworkActivityEventMap['websocket-close'];
268
+ set((state) => {
269
+ const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
270
+ if (!entry || entry.type !== 'websocket') return state;
271
+
272
+ const wsEntry = entry as WebSocketNetworkEntry;
273
+ const updatedEntry: WebSocketNetworkEntry = {
274
+ ...wsEntry,
275
+ status: 'closed',
276
+ closeCode: eventData.code,
277
+ closeReason: eventData.reason,
278
+ duration: eventData.timestamp - wsEntry.timestamp,
279
+ };
280
+
281
+ const newEntries = new Map(state.networkEntries);
282
+ newEntries.set(entry.id, updatedEntry);
283
+ return { networkEntries: newEntries };
284
+ });
285
+ break;
286
+ }
287
+
288
+ case 'websocket-message-sent': {
289
+ const eventData =
290
+ data as NetworkActivityEventMap['websocket-message-sent'];
291
+ set((state) => {
292
+ const socketId = `ws-${eventData.socketId}`;
293
+ const currentMessages = state.websocketMessages.get(socketId) || [];
294
+
295
+ const message: WebSocketMessage = {
296
+ id: getId(`${socketId}-message`),
297
+ direction: 'sent',
298
+ data: eventData.data,
299
+ messageType: eventData.messageType,
300
+ timestamp: eventData.timestamp,
301
+ };
302
+
303
+ const newMessages = new Map(state.websocketMessages);
304
+ newMessages.set(
305
+ socketId,
306
+ [...currentMessages, message].slice(
307
+ -MAX_WEBSOCKET_MESSAGES_PER_CONNECTION
308
+ )
309
+ );
310
+
311
+ return { websocketMessages: newMessages };
312
+ });
313
+ break;
314
+ }
315
+
316
+ case 'websocket-message-received': {
317
+ const eventData =
318
+ data as NetworkActivityEventMap['websocket-message-received'];
319
+ set((state) => {
320
+ const socketId = `ws-${eventData.socketId}`;
321
+ const currentMessages = state.websocketMessages.get(socketId) || [];
322
+
323
+ const message: WebSocketMessage = {
324
+ id: getId(`${socketId}-message`),
325
+ direction: 'received',
326
+ data: eventData.data,
327
+ messageType: eventData.messageType,
328
+ timestamp: eventData.timestamp,
329
+ };
330
+
331
+ const newMessages = new Map(state.websocketMessages);
332
+ newMessages.set(
333
+ socketId,
334
+ [...currentMessages, message].slice(
335
+ -MAX_WEBSOCKET_MESSAGES_PER_CONNECTION
336
+ )
337
+ );
338
+
339
+ return { websocketMessages: newMessages };
340
+ });
341
+ break;
342
+ }
343
+
344
+ case 'websocket-error': {
345
+ const eventData = data as NetworkActivityEventMap['websocket-error'];
346
+ set((state) => {
347
+ const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
348
+ if (!entry || entry.type !== 'websocket') return state;
349
+
350
+ const wsEntry = entry as WebSocketNetworkEntry;
351
+ const updatedEntry: WebSocketNetworkEntry = {
352
+ ...wsEntry,
353
+ status: 'error',
354
+ error: eventData.error,
355
+ };
356
+
357
+ const newEntries = new Map(state.networkEntries);
358
+ newEntries.set(entry.id, updatedEntry);
359
+ return { networkEntries: newEntries };
360
+ });
361
+ break;
362
+ }
363
+
364
+ case 'websocket-connection-status-changed': {
365
+ const eventData =
366
+ data as NetworkActivityEventMap['websocket-connection-status-changed'];
367
+ set((state) => {
368
+ const entry = state.networkEntries.get(`ws-${eventData.socketId}`);
369
+ if (!entry || entry.type !== 'websocket') return state;
370
+
371
+ const wsEntry = entry as WebSocketNetworkEntry;
372
+ const updatedEntry: WebSocketNetworkEntry = {
373
+ ...wsEntry,
374
+ status: eventData.status,
375
+ };
376
+
377
+ const newEntries = new Map(state.networkEntries);
378
+ newEntries.set(entry.id, updatedEntry);
379
+ return { networkEntries: newEntries };
380
+ });
381
+ break;
382
+ }
383
+
384
+ case 'sse-open': {
385
+ const eventData = data as NetworkActivityEventMap['sse-open'];
386
+ set((state) => {
387
+ const entry = state.networkEntries.get(eventData.requestId);
388
+ if (!entry || entry.type !== 'http') return state;
389
+
390
+ // Transform the existing HTTP entry to SSE
391
+ const httpEntry = entry as HttpNetworkEntry;
392
+ const sseEntry: SSENetworkEntry = {
393
+ ...httpEntry,
394
+ type: 'sse', // Change type from 'http' to 'sse'
395
+ status: 'open', // Update status
396
+ messages: [], // Add SSE-specific field
397
+ response: eventData.response,
398
+ };
399
+
400
+ const newEntries = new Map(state.networkEntries);
401
+ newEntries.set(eventData.requestId, sseEntry);
402
+ return { networkEntries: newEntries };
403
+ });
404
+ break;
405
+ }
406
+
407
+ case 'sse-message': {
408
+ const eventData = data as NetworkActivityEventMap['sse-message'];
409
+ set((state) => {
410
+ const entry = state.networkEntries.get(eventData.requestId);
411
+ if (!entry || entry.type !== 'sse') return state;
412
+
413
+ const sseEntry = entry as SSENetworkEntry;
414
+ const newMessage: SSEMessage = {
415
+ id: getId(`${eventData.requestId}-message`),
416
+ data: eventData.data,
417
+ timestamp: eventData.timestamp,
418
+ };
419
+
420
+ const updatedEntry: SSENetworkEntry = {
421
+ ...sseEntry,
422
+ messages: [...sseEntry.messages, newMessage].slice(
423
+ -MAX_SSE_MESSAGES_PER_CONNECTION
424
+ ),
425
+ };
426
+
427
+ const newEntries = new Map(state.networkEntries);
428
+ newEntries.set(eventData.requestId, updatedEntry);
429
+ return { networkEntries: newEntries };
430
+ });
431
+ break;
432
+ }
433
+
434
+ case 'sse-error': {
435
+ const eventData = data as NetworkActivityEventMap['sse-error'];
436
+ set((state) => {
437
+ const entry = state.networkEntries.get(eventData.requestId);
438
+ if (!entry || entry.type !== 'sse') return state;
439
+
440
+ const sseEntry = entry as SSENetworkEntry;
441
+ const updatedEntry: SSENetworkEntry = {
442
+ ...sseEntry,
443
+ status: 'error',
444
+ error: eventData.error.message,
445
+ };
446
+
447
+ const newEntries = new Map(state.networkEntries);
448
+ newEntries.set(eventData.requestId, updatedEntry);
449
+ return { networkEntries: newEntries };
450
+ });
451
+ break;
452
+ }
453
+
454
+ case 'sse-close': {
455
+ const eventData = data as NetworkActivityEventMap['sse-close'];
456
+ set((state) => {
457
+ const entry = state.networkEntries.get(eventData.requestId);
458
+ if (!entry || entry.type !== 'sse') return state;
459
+
460
+ const sseEntry = entry as SSENetworkEntry;
461
+ const updatedEntry: SSENetworkEntry = {
462
+ ...sseEntry,
463
+ status: 'closed',
464
+ duration: eventData.timestamp - sseEntry.timestamp,
465
+ };
466
+
467
+ const newEntries = new Map(state.networkEntries);
468
+ newEntries.set(eventData.requestId, updatedEntry);
469
+ return { networkEntries: newEntries };
470
+ });
471
+ break;
472
+ }
473
+ }
474
+ },
475
+
476
+ // Client management
477
+ client: {
478
+ setupClient: (client: NetworkActivityDevToolsClient) => {
479
+ const { handleEvent } = get();
480
+
481
+ // Subscribe to all events using the unified handler
482
+ const unsubscribeFunctions = [
483
+ client.onMessage('request-sent', (data) =>
484
+ handleEvent('request-sent', data)
485
+ ),
486
+ client.onMessage('response-received', (data) =>
487
+ handleEvent('response-received', data)
488
+ ),
489
+ client.onMessage('request-completed', (data) =>
490
+ handleEvent('request-completed', data)
491
+ ),
492
+ client.onMessage('request-failed', (data) =>
493
+ handleEvent('request-failed', data)
494
+ ),
495
+ client.onMessage('response-body', (data) =>
496
+ handleEvent('response-body', data)
497
+ ),
498
+ client.onMessage('websocket-connect', (data) =>
499
+ handleEvent('websocket-connect', data)
500
+ ),
501
+ client.onMessage('websocket-open', (data) =>
502
+ handleEvent('websocket-open', data)
503
+ ),
504
+ client.onMessage('websocket-close', (data) =>
505
+ handleEvent('websocket-close', data)
506
+ ),
507
+ client.onMessage('websocket-message-sent', (data) =>
508
+ handleEvent('websocket-message-sent', data)
509
+ ),
510
+ client.onMessage('websocket-message-received', (data) =>
511
+ handleEvent('websocket-message-received', data)
512
+ ),
513
+ client.onMessage('websocket-error', (data) =>
514
+ handleEvent('websocket-error', data)
515
+ ),
516
+ client.onMessage('websocket-connection-status-changed', (data) =>
517
+ handleEvent('websocket-connection-status-changed', data)
518
+ ),
519
+ client.onMessage('sse-open', (data) => handleEvent('sse-open', data)),
520
+ client.onMessage('sse-message', (data) =>
521
+ handleEvent('sse-message', data)
522
+ ),
523
+ client.onMessage('sse-error', (data) =>
524
+ handleEvent('sse-error', data)
525
+ ),
526
+ client.onMessage('sse-close', (data) =>
527
+ handleEvent('sse-close', data)
528
+ ),
529
+ ];
530
+
531
+ // Store unsubscribe functions in the state for cleanup
532
+ set({
533
+ _unsubscribeFunctions: unsubscribeFunctions,
534
+ _client: client,
535
+ });
536
+ },
537
+
538
+ cleanupClient: () => {
539
+ const { _unsubscribeFunctions, _client } = get();
540
+
541
+ if (_unsubscribeFunctions) {
542
+ _unsubscribeFunctions.forEach((unsubscribe: { remove: () => void }) =>
543
+ unsubscribe.remove()
544
+ );
545
+ }
546
+
547
+ if (_client) {
548
+ _client.send('network-disable', {});
549
+ }
550
+
551
+ set({
552
+ _unsubscribeFunctions: undefined,
553
+ _client: undefined,
554
+ });
555
+ },
556
+ },
557
+ }));
558
+
559
+ export const store = createNetworkActivityStore();