@replayio-app-building/netlify-recorder 0.23.0 → 0.25.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/README.md +3 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +45 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -137,12 +137,12 @@ The function is idempotent — calling it multiple times for the same request is
|
|
|
137
137
|
- **`status: "queued"` or `"processing"`** — returns `null` without re-queuing
|
|
138
138
|
- **`status: "captured"` or `"failed"`** — calls the service, updates status to `"queued"`, returns `null`
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
The `blobDataUrl` must be a direct URL to the blob JSON (e.g. an UploadThing URL). The recording container fetches this URL directly — no callback to the recorder service is involved:
|
|
141
141
|
|
|
142
142
|
```typescript
|
|
143
143
|
const recordingId = await ensureRequestRecording(sql, requestId, {
|
|
144
144
|
recorderUrl: RECORDER_URL,
|
|
145
|
-
blobDataUrl: `https://
|
|
145
|
+
blobDataUrl: `https://utfs.io/f/${blobFileKey}`, // direct URL to stored blob data
|
|
146
146
|
webhookUrl: "https://your-app.netlify.app/api/on-recording-complete", // optional
|
|
147
147
|
});
|
|
148
148
|
```
|
|
@@ -411,7 +411,7 @@ Ensures a Replay recording exists (or is being created) for a backend request. L
|
|
|
411
411
|
- `sql` — A Neon SQL tagged-template function
|
|
412
412
|
- `requestId` — The backend request UUID
|
|
413
413
|
- `options.recorderUrl` — Base URL of the Netlify Recorder service
|
|
414
|
-
- `options.blobDataUrl` —
|
|
414
|
+
- `options.blobDataUrl` — Direct URL where the recording container fetches the blob JSON (e.g. an UploadThing URL)
|
|
415
415
|
- `options.webhookUrl` — URL to POST the result to when the recording completes or fails
|
|
416
416
|
|
|
417
417
|
**Returns:** The recording ID (`string`) if the request is already recorded, or `null` if the recording was queued or is in progress.
|
package/dist/index.d.ts
CHANGED
|
@@ -279,9 +279,10 @@ interface EnsureRequestRecordingOptions {
|
|
|
279
279
|
recorderUrl: string;
|
|
280
280
|
/**
|
|
281
281
|
* Full URL where the recording container can fetch the blob JSON for this request.
|
|
282
|
-
*
|
|
282
|
+
* Must be a direct URL to the blob data (e.g. an UploadThing URL). The container
|
|
283
|
+
* fetches this URL directly — no callback to the recorder service is involved.
|
|
283
284
|
*/
|
|
284
|
-
blobDataUrl
|
|
285
|
+
blobDataUrl: string;
|
|
285
286
|
/** URL to POST the result to when the recording completes or fails. */
|
|
286
287
|
webhookUrl?: string;
|
|
287
288
|
}
|
package/dist/index.js
CHANGED
|
@@ -827,45 +827,53 @@ async function createRequestRecording(blobUrlOrData, handlerPath, requestInfo) {
|
|
|
827
827
|
);
|
|
828
828
|
}
|
|
829
829
|
let rawResult;
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
if (requestInfo.body && requestInfo.method !== "GET" && requestInfo.method !== "HEAD") {
|
|
837
|
-
reqInit.body = requestInfo.body;
|
|
838
|
-
}
|
|
839
|
-
const RequestCtor = globalThis.Request;
|
|
840
|
-
if (!RequestCtor) {
|
|
841
|
-
throw new Error(
|
|
842
|
-
"Handler uses Netlify Functions v2 signature but Request is not available in this environment. Ensure undici or a Request polyfill is installed."
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
const req = new RequestCtor(url, reqInit);
|
|
846
|
-
const context = { geo: {}, ip: "127.0.0.1", requestId: "replay", server: { region: "local" } };
|
|
847
|
-
const response = await handler(req, context);
|
|
848
|
-
if (response && typeof response === "object" && typeof response.status === "number" && typeof response.text === "function") {
|
|
849
|
-
const res = response;
|
|
850
|
-
rawResult = {
|
|
851
|
-
statusCode: res.status,
|
|
852
|
-
body: await res.text()
|
|
830
|
+
try {
|
|
831
|
+
if (isV2) {
|
|
832
|
+
const url = requestInfo.rawUrl ?? `https://localhost${requestInfo.url}`;
|
|
833
|
+
const reqInit = {
|
|
834
|
+
method: requestInfo.method,
|
|
835
|
+
headers: requestInfo.headers
|
|
853
836
|
};
|
|
837
|
+
if (requestInfo.body && requestInfo.method !== "GET" && requestInfo.method !== "HEAD") {
|
|
838
|
+
reqInit.body = requestInfo.body;
|
|
839
|
+
}
|
|
840
|
+
const RequestCtor = globalThis.Request;
|
|
841
|
+
if (!RequestCtor) {
|
|
842
|
+
throw new Error(
|
|
843
|
+
"Handler uses Netlify Functions v2 signature but Request is not available in this environment. Ensure undici or a Request polyfill is installed."
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
const req = new RequestCtor(url, reqInit);
|
|
847
|
+
const context = { geo: {}, ip: "127.0.0.1", requestId: "replay", server: { region: "local" } };
|
|
848
|
+
const response = await handler(req, context);
|
|
849
|
+
if (response && typeof response === "object" && typeof response.status === "number" && typeof response.text === "function") {
|
|
850
|
+
const res = response;
|
|
851
|
+
rawResult = {
|
|
852
|
+
statusCode: res.status,
|
|
853
|
+
body: await res.text()
|
|
854
|
+
};
|
|
855
|
+
} else {
|
|
856
|
+
rawResult = response;
|
|
857
|
+
}
|
|
854
858
|
} else {
|
|
855
|
-
rawResult =
|
|
859
|
+
rawResult = await handler({
|
|
860
|
+
httpMethod: requestInfo.method,
|
|
861
|
+
path: requestInfo.url,
|
|
862
|
+
headers: requestInfo.headers,
|
|
863
|
+
body: requestInfo.body ?? null,
|
|
864
|
+
queryStringParameters: requestInfo.queryStringParameters ?? null,
|
|
865
|
+
multiValueQueryStringParameters: requestInfo.multiValueQueryStringParameters ?? null,
|
|
866
|
+
rawUrl: requestInfo.rawUrl ?? requestInfo.url,
|
|
867
|
+
rawQuery: requestInfo.rawQuery ?? "",
|
|
868
|
+
isBase64Encoded: requestInfo.isBase64Encoded ?? false
|
|
869
|
+
});
|
|
856
870
|
}
|
|
857
|
-
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
queryStringParameters: requestInfo.queryStringParameters ?? null,
|
|
864
|
-
multiValueQueryStringParameters: requestInfo.multiValueQueryStringParameters ?? null,
|
|
865
|
-
rawUrl: requestInfo.rawUrl ?? requestInfo.url,
|
|
866
|
-
rawQuery: requestInfo.rawQuery ?? "",
|
|
867
|
-
isBase64Encoded: requestInfo.isBase64Encoded ?? false
|
|
868
|
-
});
|
|
871
|
+
} catch (handlerErr) {
|
|
872
|
+
const errorMessage = handlerErr instanceof Error ? handlerErr.message : String(handlerErr);
|
|
873
|
+
rawResult = {
|
|
874
|
+
statusCode: 500,
|
|
875
|
+
body: JSON.stringify({ error: errorMessage })
|
|
876
|
+
};
|
|
869
877
|
}
|
|
870
878
|
if (blobData.handlerResponse) {
|
|
871
879
|
const replayRes = rawResult;
|
|
@@ -1067,12 +1075,11 @@ async function ensureRequestRecording(sql, requestId, options) {
|
|
|
1067
1075
|
return null;
|
|
1068
1076
|
}
|
|
1069
1077
|
const recorderUrl = options.recorderUrl.replace(/\/+$/, "");
|
|
1070
|
-
const blobDataUrl = options.blobDataUrl ?? `${recorderUrl}/api/get-backend-request-blob?requestId=${requestId}`;
|
|
1071
1078
|
const res = await fetch(`${recorderUrl}/api/create-recording`, {
|
|
1072
1079
|
method: "POST",
|
|
1073
1080
|
headers: { "Content-Type": "application/json" },
|
|
1074
1081
|
body: JSON.stringify({
|
|
1075
|
-
blobDataUrl,
|
|
1082
|
+
blobDataUrl: options.blobDataUrl,
|
|
1076
1083
|
handlerPath: request.handler_path,
|
|
1077
1084
|
commitSha: request.commit_sha,
|
|
1078
1085
|
branchName: request.branch_name,
|