@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 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
- By default, the blob data URL points to `${recorderUrl}/api/get-backend-request-blob?requestId=${requestId}`. If your app serves blob data from a different endpoint, pass a custom `blobDataUrl`:
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://your-app.netlify.app/api/get-blob?id=${requestId}`,
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` — Override the URL where the recording container fetches the blob JSON (defaults to `${recorderUrl}/api/get-backend-request-blob?requestId=${requestId}`)
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
- * Defaults to `${recorderUrl}/api/get-backend-request-blob?requestId=${requestId}`.
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?: string;
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
- if (isV2) {
831
- const url = requestInfo.rawUrl ?? `https://localhost${requestInfo.url}`;
832
- const reqInit = {
833
- method: requestInfo.method,
834
- headers: requestInfo.headers
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 = response;
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
- } else {
858
- rawResult = await handler({
859
- httpMethod: requestInfo.method,
860
- path: requestInfo.url,
861
- headers: requestInfo.headers,
862
- body: requestInfo.body ?? null,
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replayio-app-building/netlify-recorder",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "description": "Capture and replay Netlify function executions as Replay recordings",
5
5
  "type": "module",
6
6
  "exports": {