@replayio-app-building/netlify-recorder 0.50.0 → 0.52.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/dist/index.d.ts +3 -1
- package/dist/index.js +136 -58
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -74,8 +74,10 @@ interface NetworkCall {
|
|
|
74
74
|
timestamp: number;
|
|
75
75
|
/** Epoch ms when the fetch call started. */
|
|
76
76
|
startTime: number;
|
|
77
|
-
/** Epoch ms when the response was received. */
|
|
77
|
+
/** Epoch ms when the response was received. 0 if response was never awaited (fire-and-forget). */
|
|
78
78
|
endTime: number;
|
|
79
|
+
/** True when the call was initiated but the response was not awaited by the handler. */
|
|
80
|
+
pending?: boolean;
|
|
79
81
|
}
|
|
80
82
|
interface EnvRead {
|
|
81
83
|
key: string;
|
package/dist/index.js
CHANGED
|
@@ -102,37 +102,46 @@ function patchHttpModules(mode, calls, consumed, silent) {
|
|
|
102
102
|
return origEnd(chunk, ...rest);
|
|
103
103
|
};
|
|
104
104
|
const startTime = Date.now();
|
|
105
|
+
const requestHeaders = {};
|
|
106
|
+
if (options.headers) {
|
|
107
|
+
for (const [k, v] of Object.entries(options.headers)) {
|
|
108
|
+
if (v !== void 0) requestHeaders[k] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const store = getRequestStore();
|
|
112
|
+
const targetCalls = store?.networkCalls ?? calls;
|
|
113
|
+
const entry = {
|
|
114
|
+
url: urlStr,
|
|
115
|
+
method,
|
|
116
|
+
requestHeaders,
|
|
117
|
+
requestBody: bodyChunks.length > 0 ? Buffer.concat(bodyChunks).toString("utf-8") : void 0,
|
|
118
|
+
responseStatus: 0,
|
|
119
|
+
responseHeaders: {},
|
|
120
|
+
responseBody: void 0,
|
|
121
|
+
timestamp: startTime,
|
|
122
|
+
startTime,
|
|
123
|
+
endTime: 0,
|
|
124
|
+
pending: true
|
|
125
|
+
};
|
|
126
|
+
targetCalls.push(entry);
|
|
105
127
|
req.on("response", (res) => {
|
|
106
128
|
const resChunks = [];
|
|
107
129
|
res.on("data", (chunk) => resChunks.push(chunk));
|
|
108
130
|
res.on("end", () => {
|
|
109
131
|
const endTime = Date.now();
|
|
110
|
-
const requestHeaders = {};
|
|
111
|
-
if (options.headers) {
|
|
112
|
-
for (const [k, v] of Object.entries(options.headers)) {
|
|
113
|
-
if (v !== void 0) requestHeaders[k] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
132
|
const responseHeaders = {};
|
|
117
133
|
if (res.headers) {
|
|
118
134
|
for (const [k, v] of Object.entries(res.headers)) {
|
|
119
135
|
if (v !== void 0) responseHeaders[k] = Array.isArray(v) ? v.join(", ") : String(v);
|
|
120
136
|
}
|
|
121
137
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
responseStatus: res.statusCode ?? 0,
|
|
130
|
-
responseHeaders,
|
|
131
|
-
responseBody: Buffer.concat(resChunks).toString("utf-8"),
|
|
132
|
-
timestamp: endTime,
|
|
133
|
-
startTime,
|
|
134
|
-
endTime
|
|
135
|
-
});
|
|
138
|
+
entry.requestBody = bodyChunks.length > 0 ? Buffer.concat(bodyChunks).toString("utf-8") : void 0;
|
|
139
|
+
entry.responseStatus = res.statusCode ?? 0;
|
|
140
|
+
entry.responseHeaders = responseHeaders;
|
|
141
|
+
entry.responseBody = Buffer.concat(resChunks).toString("utf-8");
|
|
142
|
+
entry.timestamp = endTime;
|
|
143
|
+
entry.endTime = endTime;
|
|
144
|
+
entry.pending = false;
|
|
136
145
|
});
|
|
137
146
|
});
|
|
138
147
|
return req;
|
|
@@ -173,11 +182,18 @@ function replayHttpRequest(url, _method, calls, consumed, callback, silent) {
|
|
|
173
182
|
);
|
|
174
183
|
}
|
|
175
184
|
consumed.add(matchIdx);
|
|
185
|
+
const isPending = call.pending || call.endTime === 0 && call.responseStatus === 0;
|
|
176
186
|
if (!silent) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
187
|
+
if (isPending) {
|
|
188
|
+
console.log(
|
|
189
|
+
` [network-replay] Consumed call ${consumed.size}/${calls.length}: ${call.method} ${call.url} => (fire-and-forget, no response captured)`
|
|
190
|
+
);
|
|
191
|
+
} else {
|
|
192
|
+
const duration = call.endTime - call.startTime;
|
|
193
|
+
console.log(
|
|
194
|
+
` [network-replay] Consumed call ${consumed.size}/${calls.length}: ${call.method} ${call.url} => ${call.responseStatus} (original: ${duration}ms)`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
181
197
|
}
|
|
182
198
|
const body = call.responseBody ?? "";
|
|
183
199
|
const fakeRes = new Readable({
|
|
@@ -186,12 +202,15 @@ function replayHttpRequest(url, _method, calls, consumed, callback, silent) {
|
|
|
186
202
|
this.push(null);
|
|
187
203
|
}
|
|
188
204
|
});
|
|
189
|
-
fakeRes.statusCode = call.responseStatus;
|
|
205
|
+
fakeRes.statusCode = isPending ? 200 : call.responseStatus;
|
|
190
206
|
fakeRes.statusMessage = "";
|
|
191
207
|
fakeRes.headers = {};
|
|
192
208
|
if (call.responseHeaders) {
|
|
209
|
+
const skipHeaders = /* @__PURE__ */ new Set(["content-encoding", "transfer-encoding"]);
|
|
193
210
|
for (const [k, v] of Object.entries(call.responseHeaders)) {
|
|
194
|
-
|
|
211
|
+
if (!skipHeaders.has(k.toLowerCase())) {
|
|
212
|
+
fakeRes.headers[k.toLowerCase()] = v;
|
|
213
|
+
}
|
|
195
214
|
}
|
|
196
215
|
}
|
|
197
216
|
const fakeReq = new Writable({
|
|
@@ -246,6 +265,20 @@ function ensureCaptureInterceptor() {
|
|
|
246
265
|
);
|
|
247
266
|
}
|
|
248
267
|
const startTime = Date.now();
|
|
268
|
+
const entry = {
|
|
269
|
+
url,
|
|
270
|
+
method,
|
|
271
|
+
requestHeaders,
|
|
272
|
+
requestBody,
|
|
273
|
+
responseStatus: 0,
|
|
274
|
+
responseHeaders: {},
|
|
275
|
+
responseBody: void 0,
|
|
276
|
+
timestamp: startTime,
|
|
277
|
+
startTime,
|
|
278
|
+
endTime: 0,
|
|
279
|
+
pending: true
|
|
280
|
+
};
|
|
281
|
+
calls.push(entry);
|
|
249
282
|
const response = await _realOriginalFetch(input, init);
|
|
250
283
|
const endTime = Date.now();
|
|
251
284
|
const responseBody = await response.clone().text();
|
|
@@ -253,18 +286,12 @@ function ensureCaptureInterceptor() {
|
|
|
253
286
|
response.headers.forEach((v, k) => {
|
|
254
287
|
responseHeaders[k] = v;
|
|
255
288
|
});
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
responseHeaders,
|
|
263
|
-
responseBody,
|
|
264
|
-
timestamp: endTime,
|
|
265
|
-
startTime,
|
|
266
|
-
endTime
|
|
267
|
-
});
|
|
289
|
+
entry.responseStatus = response.status;
|
|
290
|
+
entry.responseHeaders = responseHeaders;
|
|
291
|
+
entry.responseBody = responseBody;
|
|
292
|
+
entry.timestamp = endTime;
|
|
293
|
+
entry.endTime = endTime;
|
|
294
|
+
entry.pending = false;
|
|
268
295
|
return response;
|
|
269
296
|
};
|
|
270
297
|
globalThis.fetch = captureFetch;
|
|
@@ -320,6 +347,20 @@ function installNetworkInterceptor(mode, calls, options) {
|
|
|
320
347
|
);
|
|
321
348
|
}
|
|
322
349
|
const startTime = Date.now();
|
|
350
|
+
const entry = {
|
|
351
|
+
url,
|
|
352
|
+
method,
|
|
353
|
+
requestHeaders,
|
|
354
|
+
requestBody,
|
|
355
|
+
responseStatus: 0,
|
|
356
|
+
responseHeaders: {},
|
|
357
|
+
responseBody: void 0,
|
|
358
|
+
timestamp: startTime,
|
|
359
|
+
startTime,
|
|
360
|
+
endTime: 0,
|
|
361
|
+
pending: true
|
|
362
|
+
};
|
|
363
|
+
calls.push(entry);
|
|
323
364
|
const response = await originalFetch2(input, init);
|
|
324
365
|
const endTime = Date.now();
|
|
325
366
|
const responseBody = await response.clone().text();
|
|
@@ -327,18 +368,12 @@ function installNetworkInterceptor(mode, calls, options) {
|
|
|
327
368
|
response.headers.forEach((v, k) => {
|
|
328
369
|
responseHeaders[k] = v;
|
|
329
370
|
});
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
responseHeaders,
|
|
337
|
-
responseBody,
|
|
338
|
-
timestamp: endTime,
|
|
339
|
-
startTime,
|
|
340
|
-
endTime
|
|
341
|
-
});
|
|
371
|
+
entry.responseStatus = response.status;
|
|
372
|
+
entry.responseHeaders = responseHeaders;
|
|
373
|
+
entry.responseBody = responseBody;
|
|
374
|
+
entry.timestamp = endTime;
|
|
375
|
+
entry.endTime = endTime;
|
|
376
|
+
entry.pending = false;
|
|
342
377
|
return response;
|
|
343
378
|
};
|
|
344
379
|
globalThis.fetch = captureFetch;
|
|
@@ -373,6 +408,26 @@ function installNetworkInterceptor(mode, calls, options) {
|
|
|
373
408
|
break;
|
|
374
409
|
}
|
|
375
410
|
}
|
|
411
|
+
if (matchIdx === -1) {
|
|
412
|
+
for (let i = 0; i < calls.length; i++) {
|
|
413
|
+
if (consumed.has(i)) continue;
|
|
414
|
+
const c = calls[i];
|
|
415
|
+
if (c && c.url === url) {
|
|
416
|
+
matchIdx = i;
|
|
417
|
+
break;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
if (matchIdx === -1 && requestBody) {
|
|
422
|
+
for (let i = 0; i < calls.length; i++) {
|
|
423
|
+
if (consumed.has(i)) continue;
|
|
424
|
+
const c = calls[i];
|
|
425
|
+
if (c && c.requestBody === requestBody) {
|
|
426
|
+
matchIdx = i;
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
376
431
|
if (matchIdx === -1) {
|
|
377
432
|
for (let i = 0; i < calls.length; i++) {
|
|
378
433
|
if (!consumed.has(i)) {
|
|
@@ -388,23 +443,37 @@ function installNetworkInterceptor(mode, calls, options) {
|
|
|
388
443
|
);
|
|
389
444
|
}
|
|
390
445
|
consumed.add(matchIdx);
|
|
446
|
+
const isPending = call.pending || call.endTime === 0 && call.responseStatus === 0;
|
|
391
447
|
if (!silent) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
448
|
+
if (isPending) {
|
|
449
|
+
console.log(
|
|
450
|
+
` [network-replay] Consumed call ${consumed.size}/${calls.length}: ${call.method} ${call.url} => (fire-and-forget, no response captured)`
|
|
451
|
+
);
|
|
452
|
+
} else {
|
|
453
|
+
const duration = call.endTime - call.startTime;
|
|
454
|
+
console.log(
|
|
455
|
+
` [network-replay] Consumed call ${consumed.size}/${calls.length}: ${call.method} ${call.url} => ${call.responseStatus} (original: ${duration}ms)`
|
|
456
|
+
);
|
|
457
|
+
}
|
|
396
458
|
}
|
|
397
459
|
const body = call.responseBody ?? "";
|
|
398
|
-
const status = call.responseStatus;
|
|
460
|
+
const status = isPending ? 200 : call.responseStatus;
|
|
461
|
+
const skipHeaders = /* @__PURE__ */ new Set(["content-encoding", "transfer-encoding"]);
|
|
462
|
+
const filteredHeaders = {};
|
|
463
|
+
for (const [k, v] of Object.entries(call.responseHeaders ?? {})) {
|
|
464
|
+
if (!skipHeaders.has(k.toLowerCase())) {
|
|
465
|
+
filteredHeaders[k.toLowerCase()] = v;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
399
468
|
return {
|
|
400
469
|
ok: status >= 200 && status < 300,
|
|
401
470
|
status,
|
|
402
471
|
statusText: "",
|
|
403
472
|
headers: {
|
|
404
|
-
get: (name) =>
|
|
405
|
-
has: (name) => name.toLowerCase() in
|
|
473
|
+
get: (name) => filteredHeaders[name.toLowerCase()] ?? null,
|
|
474
|
+
has: (name) => name.toLowerCase() in filteredHeaders,
|
|
406
475
|
forEach: (cb) => {
|
|
407
|
-
for (const [k, v] of Object.entries(
|
|
476
|
+
for (const [k, v] of Object.entries(filteredHeaders)) cb(v, k);
|
|
408
477
|
}
|
|
409
478
|
},
|
|
410
479
|
text: async () => body,
|
|
@@ -1050,6 +1119,14 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo, p
|
|
|
1050
1119
|
blobData.capturedData.envReads
|
|
1051
1120
|
);
|
|
1052
1121
|
globalThis.__REPLAY_RECORDING_MODE__ = true;
|
|
1122
|
+
const origMathRandom = Math.random;
|
|
1123
|
+
let prngState = 305419896;
|
|
1124
|
+
Math.random = () => {
|
|
1125
|
+
prngState = prngState + 1831565813 | 0;
|
|
1126
|
+
let t = Math.imul(prngState ^ prngState >>> 15, 1 | prngState);
|
|
1127
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
1128
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1129
|
+
};
|
|
1053
1130
|
const g = globalThis;
|
|
1054
1131
|
if (typeof g.Blob === "undefined") {
|
|
1055
1132
|
try {
|
|
@@ -1413,6 +1490,7 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo, p
|
|
|
1413
1490
|
globalThis.__REPLAY_RECORDING_MODE__ = false;
|
|
1414
1491
|
delete g.__REPLAY_REQUEST_COOKIES__;
|
|
1415
1492
|
delete g.__REPLAY_REQUEST_HEADERS__;
|
|
1493
|
+
Math.random = origMathRandom;
|
|
1416
1494
|
networkHandle.restore();
|
|
1417
1495
|
envHandle.restore();
|
|
1418
1496
|
}
|