@rozenite/network-activity-plugin 1.0.0-alpha.1 → 1.0.0-alpha.3
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.
- package/README.md +1 -1
- package/dist/assets/panel-CU9Fj1Ml.js +16717 -0
- package/dist/assets/panel-DXGMsavf.css +555 -0
- package/dist/panel.html +2 -2
- package/dist/react-native.cjs +8 -1
- package/dist/react-native.d.ts +86 -1
- package/dist/react-native.js +6 -171
- package/dist/rozenite.json +1 -1
- package/dist/useNetworkActivityDevTools.js +488 -0
- package/package.json +4 -4
- package/react-native.ts +2 -1
- package/rozenite.config.ts +1 -1
- package/src/css-modules.d.ts +1 -1
- package/src/react-native/network-inspector.ts +391 -0
- package/src/react-native/network-requests-registry.ts +122 -0
- package/src/react-native/useNetworkActivityDevTools.ts +6 -217
- package/src/react-native/xhr-interceptor.ts +211 -0
- package/src/react-native/xml-request.d.ts +23 -0
- package/src/types/client.ts +111 -0
- package/src/types/network.ts +26 -147
- package/src/ui/components.tsx +48 -26
- package/src/ui/network-details.module.css +140 -0
- package/src/ui/network-details.tsx +252 -41
- package/src/ui/network-list.module.css +6 -0
- package/src/ui/network-list.tsx +148 -53
- package/src/ui/network-toolbar.tsx +3 -9
- package/src/ui/panel.module.css +6 -0
- package/src/ui/panel.tsx +158 -40
- package/src/ui/tanstack-query.tsx +83 -76
- package/src/ui/utils.ts +22 -13
- package/tsconfig.json +13 -6
- package/vite.config.ts +1 -1
- package/dist/assets/panel-C5YgUUj5.js +0 -54
- package/dist/assets/panel-NCVczPb1.css +0 -1
package/rozenite.config.ts
CHANGED
package/src/css-modules.d.ts
CHANGED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { NetworkActivityDevToolsClient } from '../types/client';
|
|
2
|
+
import { getNetworkRequestsRegistry } from './network-requests-registry';
|
|
3
|
+
import { XHRInterceptor } from './xhr-interceptor';
|
|
4
|
+
|
|
5
|
+
const networkRequestsRegistry = getNetworkRequestsRegistry();
|
|
6
|
+
|
|
7
|
+
const mimeTypeFromResponseType = (responseType: string): string | undefined => {
|
|
8
|
+
switch (responseType) {
|
|
9
|
+
case 'arraybuffer':
|
|
10
|
+
case 'blob':
|
|
11
|
+
case 'base64':
|
|
12
|
+
return 'application/octet-stream';
|
|
13
|
+
case 'text':
|
|
14
|
+
case '':
|
|
15
|
+
return 'text/plain';
|
|
16
|
+
case 'json':
|
|
17
|
+
return 'application/json';
|
|
18
|
+
case 'document':
|
|
19
|
+
return 'text/html';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return undefined;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const parseHeaders = (headersString: string): Record<string, string> => {
|
|
26
|
+
const headers: Record<string, string> = {};
|
|
27
|
+
if (!headersString) return headers;
|
|
28
|
+
|
|
29
|
+
const lines = headersString.split('\r\n');
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const colonIndex = line.indexOf(':');
|
|
32
|
+
if (colonIndex > 0) {
|
|
33
|
+
const key = line.substring(0, colonIndex).trim();
|
|
34
|
+
const value = line.substring(colonIndex + 1).trim();
|
|
35
|
+
headers[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const getResponseBody = async (
|
|
42
|
+
request: XMLHttpRequest
|
|
43
|
+
): Promise<{ body: string; base64Encoded: boolean }> => {
|
|
44
|
+
try {
|
|
45
|
+
if (request.responseType === 'arraybuffer') {
|
|
46
|
+
const arrayBuffer = request.response as ArrayBuffer;
|
|
47
|
+
return {
|
|
48
|
+
body: btoa(String.fromCharCode(...new Uint8Array(arrayBuffer))),
|
|
49
|
+
base64Encoded: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (request.responseType === 'blob') {
|
|
54
|
+
const contentType = request.getResponseHeader('Content-Type') || '';
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
contentType.startsWith('text/') ||
|
|
58
|
+
contentType.startsWith('application/json')
|
|
59
|
+
) {
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
const reader = new FileReader();
|
|
62
|
+
reader.onload = () => {
|
|
63
|
+
resolve({
|
|
64
|
+
body: reader.result as string,
|
|
65
|
+
base64Encoded: false,
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
reader.readAsText(request.response);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (request.responseType === 'text') {
|
|
74
|
+
return {
|
|
75
|
+
body: request.responseText || request.response || '',
|
|
76
|
+
base64Encoded: false,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
body: request.responseText || request.response || '',
|
|
82
|
+
base64Encoded: false,
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return {
|
|
86
|
+
body: `[Error reading response: ${error}]`,
|
|
87
|
+
base64Encoded: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const findRequestId = (request: XMLHttpRequest): string | null => {
|
|
93
|
+
const allRequests = networkRequestsRegistry.getAllEntries();
|
|
94
|
+
const entry = allRequests.find(({ request: req }) => req === request);
|
|
95
|
+
return entry?.id ?? null;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getInitiatorFromStack = (): {
|
|
99
|
+
type: string;
|
|
100
|
+
url?: string;
|
|
101
|
+
lineNumber?: number;
|
|
102
|
+
columnNumber?: number;
|
|
103
|
+
} => {
|
|
104
|
+
try {
|
|
105
|
+
const stack = new Error().stack;
|
|
106
|
+
if (!stack) {
|
|
107
|
+
return { type: 'other' };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const line = stack.split('\n')[9];
|
|
111
|
+
const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
|
|
112
|
+
if (match) {
|
|
113
|
+
return {
|
|
114
|
+
type: 'script',
|
|
115
|
+
url: match[2],
|
|
116
|
+
lineNumber: parseInt(match[3]),
|
|
117
|
+
columnNumber: parseInt(match[4]),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
} catch {
|
|
121
|
+
// Ignore stack parsing errors
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { type: 'other' };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type NetworkInspector = {
|
|
128
|
+
enable: () => void;
|
|
129
|
+
disable: () => void;
|
|
130
|
+
isEnabled: () => boolean;
|
|
131
|
+
dispose: () => void;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const getNetworkInspector = (
|
|
135
|
+
pluginClient: NetworkActivityDevToolsClient
|
|
136
|
+
): NetworkInspector => {
|
|
137
|
+
const generateRequestId = (): string => {
|
|
138
|
+
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const generateLoaderId = (): string => {
|
|
142
|
+
return `loader_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const enable = () => {
|
|
146
|
+
XHRInterceptor.disableInterception();
|
|
147
|
+
|
|
148
|
+
XHRInterceptor.setOpenCallback(
|
|
149
|
+
(method: string, url: string, request: XMLHttpRequest) => {
|
|
150
|
+
const requestId = generateRequestId();
|
|
151
|
+
const loaderId = generateLoaderId();
|
|
152
|
+
const startTime = Date.now();
|
|
153
|
+
const initiator = getInitiatorFromStack();
|
|
154
|
+
|
|
155
|
+
// Store request in registry with metadata
|
|
156
|
+
networkRequestsRegistry.addEntry(requestId, request, {
|
|
157
|
+
id: requestId,
|
|
158
|
+
loaderId,
|
|
159
|
+
documentURL:
|
|
160
|
+
typeof document !== 'undefined' ? document.URL : undefined,
|
|
161
|
+
method,
|
|
162
|
+
url,
|
|
163
|
+
headers: request?._headers || {},
|
|
164
|
+
startTime,
|
|
165
|
+
status: 'pending',
|
|
166
|
+
type: 'XHR',
|
|
167
|
+
initiator,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
XHRInterceptor.setSendCallback((data: string, request: XMLHttpRequest) => {
|
|
173
|
+
const requestId = findRequestId(request);
|
|
174
|
+
if (!requestId) return;
|
|
175
|
+
|
|
176
|
+
const entry = networkRequestsRegistry.getEntry(requestId);
|
|
177
|
+
if (!entry) return;
|
|
178
|
+
|
|
179
|
+
const metadata = entry.metadata;
|
|
180
|
+
|
|
181
|
+
// Update metadata with post data
|
|
182
|
+
networkRequestsRegistry.updateEntry(requestId, {
|
|
183
|
+
postData: data,
|
|
184
|
+
hasPostData: !!data,
|
|
185
|
+
headers: request?._headers || {},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Send Network.requestWillBeSent event
|
|
189
|
+
pluginClient.send('Network.requestWillBeSent', {
|
|
190
|
+
requestId,
|
|
191
|
+
loaderId: metadata.loaderId || '',
|
|
192
|
+
documentURL: metadata.documentURL || '',
|
|
193
|
+
request: {
|
|
194
|
+
url: metadata.url,
|
|
195
|
+
method: metadata.method,
|
|
196
|
+
headers: metadata.headers,
|
|
197
|
+
postData: data,
|
|
198
|
+
hasPostData: !!data,
|
|
199
|
+
},
|
|
200
|
+
timestamp: metadata.startTime,
|
|
201
|
+
wallTime: metadata.startTime,
|
|
202
|
+
initiator: metadata.initiator || { type: 'other' },
|
|
203
|
+
type: metadata.type,
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
XHRInterceptor.setHeaderReceivedCallback(
|
|
208
|
+
(
|
|
209
|
+
responseContentType: string | void,
|
|
210
|
+
responseSize: number | void,
|
|
211
|
+
allHeaders: string,
|
|
212
|
+
request: XMLHttpRequest
|
|
213
|
+
) => {
|
|
214
|
+
const requestId = findRequestId(request);
|
|
215
|
+
if (!requestId) return;
|
|
216
|
+
|
|
217
|
+
const entry = networkRequestsRegistry.getEntry(requestId);
|
|
218
|
+
if (!entry) return;
|
|
219
|
+
|
|
220
|
+
const metadata = entry.metadata;
|
|
221
|
+
const headers = parseHeaders(allHeaders);
|
|
222
|
+
const mimeType =
|
|
223
|
+
responseContentType ||
|
|
224
|
+
mimeTypeFromResponseType(request.responseType) ||
|
|
225
|
+
'text/plain';
|
|
226
|
+
|
|
227
|
+
// Update metadata with response info
|
|
228
|
+
networkRequestsRegistry.updateEntry(requestId, {
|
|
229
|
+
status: 'loading',
|
|
230
|
+
response: {
|
|
231
|
+
url: metadata.url,
|
|
232
|
+
status: request.status,
|
|
233
|
+
statusText: request.statusText,
|
|
234
|
+
headers,
|
|
235
|
+
mimeType,
|
|
236
|
+
encodedDataLength: responseSize || 0,
|
|
237
|
+
responseTime: Date.now(),
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Send Network.responseReceived event
|
|
242
|
+
pluginClient.send('Network.responseReceived', {
|
|
243
|
+
requestId,
|
|
244
|
+
loaderId: metadata.loaderId || '',
|
|
245
|
+
timestamp: Date.now(),
|
|
246
|
+
type: metadata.type || 'Other',
|
|
247
|
+
response: {
|
|
248
|
+
url: metadata.url,
|
|
249
|
+
status: request.status,
|
|
250
|
+
statusText: request.statusText,
|
|
251
|
+
headers,
|
|
252
|
+
mimeType,
|
|
253
|
+
encodedDataLength: responseSize || 0,
|
|
254
|
+
responseTime: Date.now(),
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
XHRInterceptor.setResponseCallback(
|
|
261
|
+
(
|
|
262
|
+
status: number,
|
|
263
|
+
timeout: number,
|
|
264
|
+
response: string,
|
|
265
|
+
responseURL: string,
|
|
266
|
+
responseType: string,
|
|
267
|
+
request: XMLHttpRequest
|
|
268
|
+
) => {
|
|
269
|
+
const requestId = findRequestId(request);
|
|
270
|
+
if (!requestId) return;
|
|
271
|
+
|
|
272
|
+
const entry = networkRequestsRegistry.getEntry(requestId);
|
|
273
|
+
if (!entry) return;
|
|
274
|
+
|
|
275
|
+
const metadata = entry.metadata;
|
|
276
|
+
if (!metadata) return;
|
|
277
|
+
|
|
278
|
+
const endTime = Date.now();
|
|
279
|
+
const duration = endTime - metadata.startTime;
|
|
280
|
+
const dataLength = response ? response.length : 0;
|
|
281
|
+
|
|
282
|
+
// Update metadata with final data
|
|
283
|
+
networkRequestsRegistry.updateEntry(requestId, {
|
|
284
|
+
endTime,
|
|
285
|
+
duration,
|
|
286
|
+
dataLength,
|
|
287
|
+
encodedDataLength: dataLength,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Check if request failed
|
|
291
|
+
if (status >= 400 || request.readyState === 0) {
|
|
292
|
+
const errorText = request.statusText || 'Request failed';
|
|
293
|
+
const canceled = request.readyState === 0;
|
|
294
|
+
|
|
295
|
+
// Update metadata
|
|
296
|
+
networkRequestsRegistry.updateEntry(requestId, {
|
|
297
|
+
status: 'failed',
|
|
298
|
+
errorText,
|
|
299
|
+
canceled,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Send Network.loadingFailed event
|
|
303
|
+
pluginClient.send('Network.loadingFailed', {
|
|
304
|
+
requestId,
|
|
305
|
+
timestamp: endTime,
|
|
306
|
+
type: metadata.type || 'Other',
|
|
307
|
+
errorText,
|
|
308
|
+
canceled,
|
|
309
|
+
});
|
|
310
|
+
} else {
|
|
311
|
+
// Update metadata
|
|
312
|
+
networkRequestsRegistry.updateEntry(requestId, {
|
|
313
|
+
status: 'finished',
|
|
314
|
+
encodedDataLength: dataLength,
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
// Send Network.dataReceived event if there's data
|
|
318
|
+
if (dataLength > 0) {
|
|
319
|
+
pluginClient.send('Network.dataReceived', {
|
|
320
|
+
requestId,
|
|
321
|
+
timestamp: endTime,
|
|
322
|
+
dataLength,
|
|
323
|
+
encodedDataLength: dataLength,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Send Network.loadingFinished event
|
|
328
|
+
pluginClient.send('Network.loadingFinished', {
|
|
329
|
+
requestId,
|
|
330
|
+
timestamp: endTime,
|
|
331
|
+
encodedDataLength: dataLength,
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
XHRInterceptor.enableInterception();
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const disable = () => {
|
|
341
|
+
XHRInterceptor.disableInterception();
|
|
342
|
+
networkRequestsRegistry.clear();
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
const isEnabled = () => {
|
|
346
|
+
return XHRInterceptor.isInterceptorEnabled();
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
const enableSubscription = pluginClient.onMessage('network-enable', () => {
|
|
350
|
+
enable();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const disableSubscription = pluginClient.onMessage('network-disable', () => {
|
|
354
|
+
disable();
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
const handleBodySubscription = pluginClient.onMessage(
|
|
358
|
+
'Network.getResponseBody',
|
|
359
|
+
async (payload) => {
|
|
360
|
+
const requestId = payload.requestId;
|
|
361
|
+
const entry = networkRequestsRegistry.getEntry(requestId);
|
|
362
|
+
if (!entry) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const { request } = entry;
|
|
367
|
+
const { body, base64Encoded } = await getResponseBody(request);
|
|
368
|
+
|
|
369
|
+
// Send Network.responseBodyReceived event
|
|
370
|
+
pluginClient.send('Network.responseBodyReceived', {
|
|
371
|
+
requestId,
|
|
372
|
+
body,
|
|
373
|
+
base64Encoded,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
const dispose = () => {
|
|
379
|
+
disable();
|
|
380
|
+
enableSubscription.remove();
|
|
381
|
+
disableSubscription.remove();
|
|
382
|
+
handleBodySubscription.remove();
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
enable,
|
|
387
|
+
disable,
|
|
388
|
+
isEnabled,
|
|
389
|
+
dispose,
|
|
390
|
+
};
|
|
391
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NetworkRequestId,
|
|
3
|
+
NetworkLoaderId,
|
|
4
|
+
NetworkResourceType,
|
|
5
|
+
NetworkRequest,
|
|
6
|
+
NetworkResponse,
|
|
7
|
+
NetworkInitiator,
|
|
8
|
+
} from '../types/client';
|
|
9
|
+
|
|
10
|
+
export type NetworkRequestMetadata = {
|
|
11
|
+
id: NetworkRequestId;
|
|
12
|
+
loaderId?: NetworkLoaderId;
|
|
13
|
+
documentURL?: string;
|
|
14
|
+
method: string;
|
|
15
|
+
url: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
postData?: string;
|
|
18
|
+
hasPostData?: boolean;
|
|
19
|
+
type?: NetworkResourceType;
|
|
20
|
+
initiator?: NetworkInitiator;
|
|
21
|
+
startTime: number;
|
|
22
|
+
endTime?: number;
|
|
23
|
+
duration?: number;
|
|
24
|
+
status: 'pending' | 'loading' | 'finished' | 'failed';
|
|
25
|
+
response?: NetworkResponse;
|
|
26
|
+
errorText?: string;
|
|
27
|
+
canceled?: boolean;
|
|
28
|
+
encodedDataLength?: number;
|
|
29
|
+
dataLength?: number;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type NetworkRegistryEntry = {
|
|
33
|
+
id: string;
|
|
34
|
+
request: XMLHttpRequest;
|
|
35
|
+
metadata: NetworkRequestMetadata;
|
|
36
|
+
sentAt: number;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export type NetworkRequestRegistry = {
|
|
40
|
+
addEntry: (
|
|
41
|
+
id: string,
|
|
42
|
+
request: XMLHttpRequest,
|
|
43
|
+
metadata: Partial<NetworkRequestMetadata>
|
|
44
|
+
) => void;
|
|
45
|
+
getEntry: (id: string) => NetworkRegistryEntry | null;
|
|
46
|
+
updateEntry: (id: string, updates: Partial<NetworkRequestMetadata>) => void;
|
|
47
|
+
getAllEntries: () => Array<NetworkRegistryEntry>;
|
|
48
|
+
clear: () => void;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const REQUEST_TTL = 1000 * 60 * 5; // 5 minutes
|
|
52
|
+
|
|
53
|
+
export const getNetworkRequestsRegistry = (): NetworkRequestRegistry => {
|
|
54
|
+
const registry: Map<string, NetworkRegistryEntry> = new Map();
|
|
55
|
+
|
|
56
|
+
const trimRegistry = (): void => {
|
|
57
|
+
const now = Date.now();
|
|
58
|
+
|
|
59
|
+
registry.forEach((entry) => {
|
|
60
|
+
if (now - entry.sentAt < REQUEST_TTL) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
registry.delete(entry.id);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const addEntry = (
|
|
69
|
+
id: string,
|
|
70
|
+
request: XMLHttpRequest,
|
|
71
|
+
metadata: Partial<NetworkRequestMetadata>
|
|
72
|
+
) => {
|
|
73
|
+
trimRegistry();
|
|
74
|
+
|
|
75
|
+
const fullMetadata: NetworkRequestMetadata = {
|
|
76
|
+
id,
|
|
77
|
+
method: metadata.method || 'GET',
|
|
78
|
+
url: metadata.url || '',
|
|
79
|
+
headers: metadata.headers || {},
|
|
80
|
+
startTime: metadata.startTime || Date.now(),
|
|
81
|
+
status: metadata.status || 'pending',
|
|
82
|
+
...metadata,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
registry.set(id, {
|
|
86
|
+
id,
|
|
87
|
+
request,
|
|
88
|
+
metadata: fullMetadata,
|
|
89
|
+
sentAt: Date.now(),
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const getEntry = (id: string) => {
|
|
94
|
+
return registry.get(id) ?? null;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const updateEntry = (
|
|
98
|
+
id: string,
|
|
99
|
+
updates: Partial<NetworkRequestMetadata>
|
|
100
|
+
) => {
|
|
101
|
+
const entry = registry.get(id);
|
|
102
|
+
if (entry) {
|
|
103
|
+
entry.metadata = { ...entry.metadata, ...updates };
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const getAllEntries = () => {
|
|
108
|
+
return Array.from(registry.values());
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const clear = () => {
|
|
112
|
+
registry.clear();
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
addEntry,
|
|
117
|
+
getEntry,
|
|
118
|
+
updateEntry,
|
|
119
|
+
getAllEntries,
|
|
120
|
+
clear,
|
|
121
|
+
};
|
|
122
|
+
};
|