@replayio-app-building/netlify-recorder 0.37.0 → 0.38.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 +4 -0
- package/dist/index.js +66 -2
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -351,6 +351,10 @@ interface RecordingEndpointResponse {
|
|
|
351
351
|
* needed and returns the current status. Idempotent: re-posting the same
|
|
352
352
|
* request ID will not re-queue an already-queued or recorded request.
|
|
353
353
|
*
|
|
354
|
+
* **POST** with `?_callback=1&requestId=<uuid>` — webhook callback from the
|
|
355
|
+
* recorder service. Updates `backend_requests` status and optionally forwards
|
|
356
|
+
* to the configured `webhookUrl`.
|
|
357
|
+
*
|
|
354
358
|
* **GET** with `?requestId=<uuid>` — returns the current recording status
|
|
355
359
|
* without triggering any recording.
|
|
356
360
|
*
|
package/dist/index.js
CHANGED
|
@@ -890,11 +890,21 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo) {
|
|
|
890
890
|
const nodeCrypto = __require("crypto");
|
|
891
891
|
g.crypto = {
|
|
892
892
|
randomUUID: () => nodeCrypto.randomUUID(),
|
|
893
|
-
getRandomValues: (buf) => nodeCrypto.getRandomValues(buf)
|
|
893
|
+
getRandomValues: (buf) => nodeCrypto.getRandomValues(buf),
|
|
894
|
+
subtle: nodeCrypto.webcrypto?.subtle
|
|
894
895
|
};
|
|
895
896
|
} catch {
|
|
896
897
|
}
|
|
897
898
|
}
|
|
899
|
+
if (g.crypto && typeof g.crypto.subtle === "undefined") {
|
|
900
|
+
try {
|
|
901
|
+
const nodeCrypto = __require("crypto");
|
|
902
|
+
if (nodeCrypto.webcrypto?.subtle) {
|
|
903
|
+
g.crypto.subtle = nodeCrypto.webcrypto.subtle;
|
|
904
|
+
}
|
|
905
|
+
} catch {
|
|
906
|
+
}
|
|
907
|
+
}
|
|
898
908
|
if (typeof g.Headers === "undefined") {
|
|
899
909
|
const HeadersShim = class Headers {
|
|
900
910
|
_map;
|
|
@@ -1073,6 +1083,9 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo) {
|
|
|
1073
1083
|
);
|
|
1074
1084
|
}
|
|
1075
1085
|
const req = new RequestCtor(url, reqInit);
|
|
1086
|
+
const cookieKey = Object.keys(requestInfo.headers).find((k) => k.toLowerCase() === "cookie");
|
|
1087
|
+
g.__REPLAY_REQUEST_COOKIES__ = cookieKey ? requestInfo.headers[cookieKey] : "";
|
|
1088
|
+
g.__REPLAY_REQUEST_HEADERS__ = requestInfo.headers;
|
|
1076
1089
|
const context = { geo: {}, ip: "127.0.0.1", requestId: "replay", server: { region: "local" } };
|
|
1077
1090
|
const response = await handler(req, context);
|
|
1078
1091
|
if (response && typeof response === "object" && typeof response.status === "number" && typeof response.text === "function") {
|
|
@@ -1162,6 +1175,8 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo) {
|
|
|
1162
1175
|
console.log(` [network-replay] All ${total} recorded network call(s) were consumed.`);
|
|
1163
1176
|
}
|
|
1164
1177
|
globalThis.__REPLAY_RECORDING_MODE__ = false;
|
|
1178
|
+
delete g.__REPLAY_REQUEST_COOKIES__;
|
|
1179
|
+
delete g.__REPLAY_REQUEST_HEADERS__;
|
|
1165
1180
|
networkHandle.restore();
|
|
1166
1181
|
envHandle.restore();
|
|
1167
1182
|
}
|
|
@@ -1484,6 +1499,16 @@ function formatStatus(request) {
|
|
|
1484
1499
|
}
|
|
1485
1500
|
return { status: "pending", requestId: request.id };
|
|
1486
1501
|
}
|
|
1502
|
+
async function forwardWebhook(url, payload) {
|
|
1503
|
+
try {
|
|
1504
|
+
await fetch(url, {
|
|
1505
|
+
method: "POST",
|
|
1506
|
+
headers: { "Content-Type": "application/json" },
|
|
1507
|
+
body: JSON.stringify(payload)
|
|
1508
|
+
});
|
|
1509
|
+
} catch {
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1487
1512
|
function createRecordingEndpoint(options) {
|
|
1488
1513
|
const { sql, recorderUrl, secret, webhookUrl } = options;
|
|
1489
1514
|
return async (req) => {
|
|
@@ -1507,6 +1532,44 @@ function createRecordingEndpoint(options) {
|
|
|
1507
1532
|
return jsonResponse(formatStatus(request), 200);
|
|
1508
1533
|
}
|
|
1509
1534
|
if (req.method === "POST") {
|
|
1535
|
+
const url = new URL(req.url);
|
|
1536
|
+
if (url.searchParams.has("_callback")) {
|
|
1537
|
+
const callbackRequestId = url.searchParams.get("requestId");
|
|
1538
|
+
if (!callbackRequestId) {
|
|
1539
|
+
return jsonResponse({ status: "error", error: "Missing requestId in callback" }, 400);
|
|
1540
|
+
}
|
|
1541
|
+
const callbackBody = await req.json();
|
|
1542
|
+
if (callbackBody.status === "recorded" && callbackBody.recordingId) {
|
|
1543
|
+
await backendRequestsUpdateStatus(
|
|
1544
|
+
sql,
|
|
1545
|
+
callbackRequestId,
|
|
1546
|
+
"recorded",
|
|
1547
|
+
callbackBody.recordingId
|
|
1548
|
+
);
|
|
1549
|
+
if (webhookUrl) {
|
|
1550
|
+
await forwardWebhook(webhookUrl, {
|
|
1551
|
+
status: "recorded",
|
|
1552
|
+
recordingId: callbackBody.recordingId
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
return jsonResponse(
|
|
1556
|
+
{ status: "recorded", recordingId: callbackBody.recordingId, requestId: callbackRequestId },
|
|
1557
|
+
200
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
if (callbackBody.status === "failed") {
|
|
1561
|
+
const errorMsg = callbackBody.error ?? "Recording failed";
|
|
1562
|
+
await backendRequestsUpdateStatus(sql, callbackRequestId, "failed", void 0, errorMsg);
|
|
1563
|
+
if (webhookUrl) {
|
|
1564
|
+
await forwardWebhook(webhookUrl, { status: "failed", error: errorMsg });
|
|
1565
|
+
}
|
|
1566
|
+
return jsonResponse(
|
|
1567
|
+
{ status: "error", requestId: callbackRequestId, error: errorMsg },
|
|
1568
|
+
200
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
return jsonResponse({ status: "error", error: "Invalid callback payload" }, 400);
|
|
1572
|
+
}
|
|
1510
1573
|
const body = await req.json();
|
|
1511
1574
|
const requestId = body.requestId;
|
|
1512
1575
|
if (!requestId) {
|
|
@@ -1525,10 +1588,11 @@ function createRecordingEndpoint(options) {
|
|
|
1525
1588
|
if (request.status === "queued" || request.status === "processing") {
|
|
1526
1589
|
return jsonResponse({ status: "pending", requestId }, 200);
|
|
1527
1590
|
}
|
|
1591
|
+
const selfCallbackUrl = `${url.origin}${url.pathname}?_callback=1&requestId=${encodeURIComponent(requestId)}`;
|
|
1528
1592
|
try {
|
|
1529
1593
|
const recordingId = await ensureRequestRecording(sql, requestId, {
|
|
1530
1594
|
recorderUrl,
|
|
1531
|
-
webhookUrl
|
|
1595
|
+
webhookUrl: selfCallbackUrl
|
|
1532
1596
|
});
|
|
1533
1597
|
if (recordingId) {
|
|
1534
1598
|
return jsonResponse({ status: "recorded", recordingId, requestId }, 200);
|