@capillarytech/cap-ui-dev-tools 1.5.0 → 1.6.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.
- package/package.json +1 -1
- package/src/capvision-recorder/adapters/WebdriverIOAdapter.js +94 -2
- package/src/capvision-recorder/core/CapVisionRecorder.js +7 -275
- package/src/capvision-recorder/core/ReportEnhancer.js +0 -57
- package/src/capvision-recorder/index.js +0 -3
- package/src/capvision-recorder/assets/capvision-plugins/network-record.min.js +0 -542
- package/src/capvision-recorder/assets/capvision-plugins/network-replay.min.js +0 -434
|
@@ -1,542 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RRWeb Network Record Plugin
|
|
3
|
-
* Captures XHR and Fetch network requests during recording
|
|
4
|
-
* Based on: https://github.com/rrweb-io/rrweb/pull/1689
|
|
5
|
-
*/
|
|
6
|
-
(function (global, factory) {
|
|
7
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
8
|
-
typeof define === 'function' && define.amd ? define(factory) :
|
|
9
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.rrwebPluginNetworkRecord = factory());
|
|
10
|
-
})(this, (function () {
|
|
11
|
-
'use strict';
|
|
12
|
-
|
|
13
|
-
function getRecordNetworkPlugin(options) {
|
|
14
|
-
const opts = options || {};
|
|
15
|
-
const recordBody = opts.recordBody !== false;
|
|
16
|
-
const recordHeaders = opts.recordHeaders !== false;
|
|
17
|
-
const recordInitiator = opts.recordInitiator !== false;
|
|
18
|
-
const recordPerformance = opts.recordPerformance !== false;
|
|
19
|
-
const maxBodyLength = opts.maxBodyLength || 10000;
|
|
20
|
-
const ignoreRequestFn = opts.ignoreRequestFn || (() => false);
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
name: 'rrweb/network@1',
|
|
24
|
-
observer(emit, win) {
|
|
25
|
-
const window = win || globalThis;
|
|
26
|
-
|
|
27
|
-
// Verify emit is a function
|
|
28
|
-
if (typeof emit !== 'function') {
|
|
29
|
-
return () => {};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Helper: Truncate body if too large
|
|
33
|
-
function truncateBody(body, maxLength) {
|
|
34
|
-
if (!body) return body;
|
|
35
|
-
const bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
|
|
36
|
-
if (bodyStr.length > maxLength) {
|
|
37
|
-
return bodyStr.substring(0, maxLength) + '... [truncated]';
|
|
38
|
-
}
|
|
39
|
-
return bodyStr;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Helper: Get initiator stack trace
|
|
43
|
-
function getInitiator() {
|
|
44
|
-
if (!recordInitiator) return undefined;
|
|
45
|
-
try {
|
|
46
|
-
const stack = new Error().stack;
|
|
47
|
-
if (stack) {
|
|
48
|
-
const lines = stack.split('\n').slice(3, 6);
|
|
49
|
-
return lines.join('\n');
|
|
50
|
-
}
|
|
51
|
-
} catch (e) {
|
|
52
|
-
// Ignore
|
|
53
|
-
}
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Helper: Get performance entry
|
|
58
|
-
function getPerformanceEntry(url, startTime) {
|
|
59
|
-
if (!recordPerformance || !window.performance || !window.performance.getEntriesByType) {
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
const entries = window.performance.getEntriesByType('resource');
|
|
64
|
-
const entry = entries.find(e =>
|
|
65
|
-
e.name === url &&
|
|
66
|
-
Math.abs(e.startTime - startTime) < 100
|
|
67
|
-
);
|
|
68
|
-
if (entry) {
|
|
69
|
-
return {
|
|
70
|
-
duration: entry.duration,
|
|
71
|
-
transferSize: entry.transferSize,
|
|
72
|
-
encodedBodySize: entry.encodedBodySize,
|
|
73
|
-
decodedBodySize: entry.decodedBodySize,
|
|
74
|
-
startTime: entry.startTime,
|
|
75
|
-
responseStart: entry.responseStart,
|
|
76
|
-
responseEnd: entry.responseEnd
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
} catch (e) {
|
|
80
|
-
// Ignore
|
|
81
|
-
}
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Store original functions
|
|
86
|
-
const originalFetch = window.fetch;
|
|
87
|
-
const originalXHROpen = window.XMLHttpRequest.prototype.open;
|
|
88
|
-
const originalXHRSend = window.XMLHttpRequest.prototype.send;
|
|
89
|
-
|
|
90
|
-
// PerformanceObserver to catch ALL network resources (from PR #1105)
|
|
91
|
-
// This catches requests that don't go through fetch/XHR (images, scripts, etc.)
|
|
92
|
-
let performanceObserver = null;
|
|
93
|
-
if (window.PerformanceObserver && window.performance) {
|
|
94
|
-
try {
|
|
95
|
-
performanceObserver = new PerformanceObserver((list) => {
|
|
96
|
-
for (const entry of list.getEntries()) {
|
|
97
|
-
// Only capture resource entries (not navigation)
|
|
98
|
-
if (entry.entryType === 'resource') {
|
|
99
|
-
const resourceEntry = entry;
|
|
100
|
-
|
|
101
|
-
// Skip if filtered
|
|
102
|
-
if (ignoreRequestFn(resourceEntry.name, resourceEntry.initiatorType || 'other')) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Skip fetch/XHR as they're handled by patching
|
|
107
|
-
if (resourceEntry.initiatorType === 'xmlhttprequest' || resourceEntry.initiatorType === 'fetch') {
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Emit request event for resource
|
|
112
|
-
try {
|
|
113
|
-
const requestId = `resource_${resourceEntry.startTime}_${Math.random().toString(36).substr(2, 9)}`;
|
|
114
|
-
const method = 'GET'; // Resources are typically GET requests
|
|
115
|
-
|
|
116
|
-
emit({
|
|
117
|
-
type: 6,
|
|
118
|
-
data: {
|
|
119
|
-
plugin: 'rrweb/network@1',
|
|
120
|
-
payload: {
|
|
121
|
-
type: 'request',
|
|
122
|
-
id: requestId,
|
|
123
|
-
method: method,
|
|
124
|
-
url: resourceEntry.name,
|
|
125
|
-
timestamp: resourceEntry.startTime,
|
|
126
|
-
requestType: resourceEntry.initiatorType || 'other',
|
|
127
|
-
initiator: recordInitiator ? resourceEntry.name : undefined
|
|
128
|
-
}
|
|
129
|
-
},
|
|
130
|
-
timestamp: resourceEntry.startTime
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Emit response event
|
|
134
|
-
emit({
|
|
135
|
-
type: 6,
|
|
136
|
-
data: {
|
|
137
|
-
plugin: 'rrweb/network@1',
|
|
138
|
-
payload: {
|
|
139
|
-
type: 'response',
|
|
140
|
-
id: requestId,
|
|
141
|
-
status: resourceEntry.responseStatus || 200,
|
|
142
|
-
statusText: 'OK',
|
|
143
|
-
timestamp: resourceEntry.responseEnd || resourceEntry.startTime + resourceEntry.duration,
|
|
144
|
-
duration: resourceEntry.duration,
|
|
145
|
-
performance: {
|
|
146
|
-
duration: resourceEntry.duration,
|
|
147
|
-
transferSize: resourceEntry.transferSize,
|
|
148
|
-
encodedBodySize: resourceEntry.encodedBodySize,
|
|
149
|
-
decodedBodySize: resourceEntry.decodedBodySize,
|
|
150
|
-
startTime: resourceEntry.startTime,
|
|
151
|
-
responseStart: resourceEntry.responseStart,
|
|
152
|
-
responseEnd: resourceEntry.responseEnd
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
},
|
|
156
|
-
timestamp: resourceEntry.responseEnd || resourceEntry.startTime + resourceEntry.duration
|
|
157
|
-
});
|
|
158
|
-
} catch (e) {
|
|
159
|
-
// Ignore errors
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
// Observe resource timing entries
|
|
166
|
-
performanceObserver.observe({ entryTypes: ['resource'] });
|
|
167
|
-
} catch (e) {
|
|
168
|
-
// PerformanceObserver not supported or error
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Capture navigation timing (from PR #1105)
|
|
173
|
-
if (window.performance && window.performance.getEntriesByType) {
|
|
174
|
-
try {
|
|
175
|
-
const navEntries = window.performance.getEntriesByType('navigation');
|
|
176
|
-
if (navEntries.length > 0) {
|
|
177
|
-
const navEntry = navEntries[0];
|
|
178
|
-
if (navEntry && !ignoreRequestFn(window.location.href, 'navigation')) {
|
|
179
|
-
try {
|
|
180
|
-
const requestId = `navigation_${navEntry.startTime}`;
|
|
181
|
-
|
|
182
|
-
emit({
|
|
183
|
-
type: 6,
|
|
184
|
-
data: {
|
|
185
|
-
plugin: 'rrweb/network@1',
|
|
186
|
-
payload: {
|
|
187
|
-
type: 'request',
|
|
188
|
-
id: requestId,
|
|
189
|
-
method: 'GET',
|
|
190
|
-
url: window.location.href,
|
|
191
|
-
timestamp: navEntry.startTime,
|
|
192
|
-
requestType: 'navigation'
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
timestamp: navEntry.startTime
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
emit({
|
|
199
|
-
type: 6,
|
|
200
|
-
data: {
|
|
201
|
-
plugin: 'rrweb/network@1',
|
|
202
|
-
payload: {
|
|
203
|
-
type: 'response',
|
|
204
|
-
id: requestId,
|
|
205
|
-
status: 200,
|
|
206
|
-
statusText: 'OK',
|
|
207
|
-
timestamp: navEntry.loadEventEnd || navEntry.startTime + navEntry.duration,
|
|
208
|
-
duration: navEntry.duration,
|
|
209
|
-
performance: {
|
|
210
|
-
duration: navEntry.duration,
|
|
211
|
-
transferSize: navEntry.transferSize,
|
|
212
|
-
encodedBodySize: navEntry.encodedBodySize,
|
|
213
|
-
decodedBodySize: navEntry.decodedBodySize,
|
|
214
|
-
startTime: navEntry.startTime,
|
|
215
|
-
responseStart: navEntry.responseStart,
|
|
216
|
-
responseEnd: navEntry.responseEnd
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
timestamp: navEntry.loadEventEnd || navEntry.startTime + navEntry.duration
|
|
221
|
-
});
|
|
222
|
-
} catch (e) {
|
|
223
|
-
// Ignore errors
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
} catch (e) {
|
|
228
|
-
// Ignore errors
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Wrap fetch
|
|
233
|
-
window.fetch = function(...args) {
|
|
234
|
-
// Extract URL
|
|
235
|
-
let url = null;
|
|
236
|
-
if (typeof args[0] === 'string') {
|
|
237
|
-
url = args[0];
|
|
238
|
-
} else if (args[0] instanceof Request) {
|
|
239
|
-
url = args[0].url;
|
|
240
|
-
} else if (args[0] instanceof URL) {
|
|
241
|
-
url = args[0].href;
|
|
242
|
-
} else if (args[0]?.url) {
|
|
243
|
-
url = args[0].url;
|
|
244
|
-
} else if (args[0]?.href) {
|
|
245
|
-
url = args[0].href;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const init = args[1] || {};
|
|
249
|
-
|
|
250
|
-
if (!url) {
|
|
251
|
-
return originalFetch.apply(this, args);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
if (ignoreRequestFn(url, 'fetch')) {
|
|
255
|
-
return originalFetch.apply(this, args);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
const startTime = Date.now();
|
|
259
|
-
const requestId = `fetch_${startTime}_${Math.random().toString(36).substr(2, 9)}`;
|
|
260
|
-
const initiator = getInitiator();
|
|
261
|
-
const method = init.method || 'GET';
|
|
262
|
-
|
|
263
|
-
// Emit request event
|
|
264
|
-
try {
|
|
265
|
-
emit({
|
|
266
|
-
type: 6,
|
|
267
|
-
data: {
|
|
268
|
-
plugin: 'rrweb/network@1',
|
|
269
|
-
payload: {
|
|
270
|
-
type: 'request',
|
|
271
|
-
id: requestId,
|
|
272
|
-
method: method,
|
|
273
|
-
url: url,
|
|
274
|
-
headers: recordHeaders ? (init.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : init.headers) : undefined,
|
|
275
|
-
body: recordBody ? truncateBody(init.body, maxBodyLength) : undefined,
|
|
276
|
-
timestamp: startTime,
|
|
277
|
-
initiator: initiator,
|
|
278
|
-
requestType: 'fetch'
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
timestamp: startTime
|
|
282
|
-
});
|
|
283
|
-
} catch (e) {
|
|
284
|
-
// Ignore
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
return originalFetch.apply(this, args).then(
|
|
288
|
-
response => {
|
|
289
|
-
const endTime = Date.now();
|
|
290
|
-
const performanceEntry = getPerformanceEntry(url, startTime);
|
|
291
|
-
|
|
292
|
-
// Clone response to read body
|
|
293
|
-
const clonedResponse = response.clone();
|
|
294
|
-
|
|
295
|
-
// Read response body if needed
|
|
296
|
-
if (recordBody) {
|
|
297
|
-
clonedResponse.text().then(body => {
|
|
298
|
-
try {
|
|
299
|
-
emit({
|
|
300
|
-
type: 6,
|
|
301
|
-
data: {
|
|
302
|
-
plugin: 'rrweb/network@1',
|
|
303
|
-
payload: {
|
|
304
|
-
type: 'response',
|
|
305
|
-
id: requestId,
|
|
306
|
-
status: response.status,
|
|
307
|
-
statusText: response.statusText,
|
|
308
|
-
headers: recordHeaders ? Object.fromEntries(response.headers.entries()) : undefined,
|
|
309
|
-
body: truncateBody(body, maxBodyLength),
|
|
310
|
-
timestamp: endTime,
|
|
311
|
-
duration: endTime - startTime,
|
|
312
|
-
performance: performanceEntry
|
|
313
|
-
}
|
|
314
|
-
},
|
|
315
|
-
timestamp: endTime
|
|
316
|
-
});
|
|
317
|
-
} catch (e) {
|
|
318
|
-
// Ignore
|
|
319
|
-
}
|
|
320
|
-
}).catch(() => {
|
|
321
|
-
// If body read fails, emit without body
|
|
322
|
-
try {
|
|
323
|
-
emit({
|
|
324
|
-
type: 6,
|
|
325
|
-
data: {
|
|
326
|
-
plugin: 'rrweb/network@1',
|
|
327
|
-
payload: {
|
|
328
|
-
type: 'response',
|
|
329
|
-
id: requestId,
|
|
330
|
-
status: response.status,
|
|
331
|
-
statusText: response.statusText,
|
|
332
|
-
headers: recordHeaders ? Object.fromEntries(response.headers.entries()) : undefined,
|
|
333
|
-
timestamp: endTime,
|
|
334
|
-
duration: endTime - startTime,
|
|
335
|
-
performance: performanceEntry
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
timestamp: endTime
|
|
339
|
-
});
|
|
340
|
-
} catch (e) {
|
|
341
|
-
// Ignore
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
} else {
|
|
345
|
-
try {
|
|
346
|
-
emit({
|
|
347
|
-
type: 6,
|
|
348
|
-
data: {
|
|
349
|
-
plugin: 'rrweb/network@1',
|
|
350
|
-
payload: {
|
|
351
|
-
type: 'response',
|
|
352
|
-
id: requestId,
|
|
353
|
-
status: response.status,
|
|
354
|
-
statusText: response.statusText,
|
|
355
|
-
headers: recordHeaders ? Object.fromEntries(response.headers.entries()) : undefined,
|
|
356
|
-
timestamp: endTime,
|
|
357
|
-
duration: endTime - startTime,
|
|
358
|
-
performance: performanceEntry
|
|
359
|
-
}
|
|
360
|
-
},
|
|
361
|
-
timestamp: endTime
|
|
362
|
-
});
|
|
363
|
-
} catch (e) {
|
|
364
|
-
// Ignore
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
return response;
|
|
369
|
-
},
|
|
370
|
-
error => {
|
|
371
|
-
const endTime = Date.now();
|
|
372
|
-
try {
|
|
373
|
-
emit({
|
|
374
|
-
type: 6,
|
|
375
|
-
data: {
|
|
376
|
-
plugin: 'rrweb/network@1',
|
|
377
|
-
payload: {
|
|
378
|
-
type: 'response',
|
|
379
|
-
id: requestId,
|
|
380
|
-
error: error.message || 'Network request failed',
|
|
381
|
-
timestamp: endTime,
|
|
382
|
-
duration: endTime - startTime
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
timestamp: endTime
|
|
386
|
-
});
|
|
387
|
-
} catch (e) {
|
|
388
|
-
// Ignore
|
|
389
|
-
}
|
|
390
|
-
throw error;
|
|
391
|
-
}
|
|
392
|
-
);
|
|
393
|
-
};
|
|
394
|
-
|
|
395
|
-
// Wrap XMLHttpRequest prototype methods
|
|
396
|
-
window.XMLHttpRequest.prototype.open = function(method, url, ...rest) {
|
|
397
|
-
// Handle relative URLs - resolve them
|
|
398
|
-
let resolvedUrl = url;
|
|
399
|
-
if (url && typeof url === 'string' && !url.startsWith('http://') && !url.startsWith('https://') && !url.startsWith('//')) {
|
|
400
|
-
try {
|
|
401
|
-
resolvedUrl = new URL(url, window.location.href).href;
|
|
402
|
-
} catch (e) {
|
|
403
|
-
resolvedUrl = url;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Store network data on the XHR instance
|
|
408
|
-
this._rrwebNetworkData = {
|
|
409
|
-
method: method,
|
|
410
|
-
url: resolvedUrl || url,
|
|
411
|
-
startTime: Date.now(),
|
|
412
|
-
requestId: `xhr_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
413
|
-
initiator: getInitiator()
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
return originalXHROpen.apply(this, [method, url, ...rest]);
|
|
417
|
-
};
|
|
418
|
-
|
|
419
|
-
window.XMLHttpRequest.prototype.send = function(body) {
|
|
420
|
-
const xhr = this;
|
|
421
|
-
const data = xhr._rrwebNetworkData;
|
|
422
|
-
|
|
423
|
-
if (!data) {
|
|
424
|
-
return originalXHRSend.apply(this, arguments);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (ignoreRequestFn(data.url, 'xhr')) {
|
|
428
|
-
return originalXHRSend.apply(this, arguments);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Emit request event
|
|
432
|
-
try {
|
|
433
|
-
emit({
|
|
434
|
-
type: 6,
|
|
435
|
-
data: {
|
|
436
|
-
plugin: 'rrweb/network@1',
|
|
437
|
-
payload: {
|
|
438
|
-
type: 'request',
|
|
439
|
-
id: data.requestId,
|
|
440
|
-
method: data.method,
|
|
441
|
-
url: data.url,
|
|
442
|
-
headers: recordHeaders ? this.getAllResponseHeaders() : undefined,
|
|
443
|
-
body: recordBody ? truncateBody(body, maxBodyLength) : undefined,
|
|
444
|
-
timestamp: data.startTime,
|
|
445
|
-
initiator: data.initiator,
|
|
446
|
-
requestType: 'xhr'
|
|
447
|
-
}
|
|
448
|
-
},
|
|
449
|
-
timestamp: data.startTime
|
|
450
|
-
});
|
|
451
|
-
} catch (e) {
|
|
452
|
-
// Ignore
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
// Listen for response
|
|
456
|
-
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
457
|
-
xhr.onreadystatechange = function(...args) {
|
|
458
|
-
if (xhr.readyState === 4) {
|
|
459
|
-
const endTime = Date.now();
|
|
460
|
-
const performanceEntry = getPerformanceEntry(data.url, data.startTime);
|
|
461
|
-
|
|
462
|
-
try {
|
|
463
|
-
emit({
|
|
464
|
-
type: 6,
|
|
465
|
-
data: {
|
|
466
|
-
plugin: 'rrweb/network@1',
|
|
467
|
-
payload: {
|
|
468
|
-
type: 'response',
|
|
469
|
-
id: data.requestId,
|
|
470
|
-
status: xhr.status,
|
|
471
|
-
statusText: xhr.statusText,
|
|
472
|
-
headers: recordHeaders ? xhr.getAllResponseHeaders() : undefined,
|
|
473
|
-
body: recordBody ? truncateBody(xhr.responseText, maxBodyLength) : undefined,
|
|
474
|
-
timestamp: endTime,
|
|
475
|
-
duration: endTime - data.startTime,
|
|
476
|
-
performance: performanceEntry
|
|
477
|
-
}
|
|
478
|
-
},
|
|
479
|
-
timestamp: endTime
|
|
480
|
-
});
|
|
481
|
-
} catch (e) {
|
|
482
|
-
// Ignore
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
if (originalOnReadyStateChange) {
|
|
486
|
-
return originalOnReadyStateChange.apply(this, args);
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
|
|
490
|
-
// Listen for errors
|
|
491
|
-
const originalOnError = xhr.onerror;
|
|
492
|
-
xhr.onerror = function(...args) {
|
|
493
|
-
const endTime = Date.now();
|
|
494
|
-
try {
|
|
495
|
-
emit({
|
|
496
|
-
type: 6,
|
|
497
|
-
data: {
|
|
498
|
-
plugin: 'rrweb/network@1',
|
|
499
|
-
payload: {
|
|
500
|
-
type: 'response',
|
|
501
|
-
id: data.requestId,
|
|
502
|
-
error: 'Network request failed',
|
|
503
|
-
timestamp: endTime,
|
|
504
|
-
duration: endTime - data.startTime
|
|
505
|
-
}
|
|
506
|
-
},
|
|
507
|
-
timestamp: endTime
|
|
508
|
-
});
|
|
509
|
-
} catch (e) {
|
|
510
|
-
// Ignore
|
|
511
|
-
}
|
|
512
|
-
if (originalOnError) {
|
|
513
|
-
return originalOnError.apply(this, args);
|
|
514
|
-
}
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
return originalXHRSend.apply(this, arguments);
|
|
518
|
-
};
|
|
519
|
-
|
|
520
|
-
// Return cleanup function
|
|
521
|
-
return () => {
|
|
522
|
-
try {
|
|
523
|
-
// Disconnect PerformanceObserver
|
|
524
|
-
if (performanceObserver) {
|
|
525
|
-
performanceObserver.disconnect();
|
|
526
|
-
performanceObserver = null;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
// Restore original functions
|
|
530
|
-
window.fetch = originalFetch;
|
|
531
|
-
window.XMLHttpRequest.prototype.open = originalXHROpen;
|
|
532
|
-
window.XMLHttpRequest.prototype.send = originalXHRSend;
|
|
533
|
-
} catch (e) {
|
|
534
|
-
// Ignore
|
|
535
|
-
}
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return getRecordNetworkPlugin;
|
|
542
|
-
}));
|