@replayio-app-building/netlify-recorder 0.15.4 → 0.15.6

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.
Files changed (2) hide show
  1. package/README.md +26 -11
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -31,8 +31,9 @@ The Netlify Recorder app (`https://netlify-recorder-bm4wmw.netlify.app`) provide
31
31
  | `REPLAY_REPOSITORY_URL` | Your app's git repository URL (e.g. `https://github.com/org/repo.git`) | Set in your deploy script or Netlify site settings |
32
32
  | `COMMIT_SHA` | The git commit hash of the deployed code | Set in your deploy script via `git rev-parse HEAD` |
33
33
  | `BRANCH_NAME` | The git branch of the deployed code | Set in your deploy script via `git rev-parse --abbrev-ref HEAD` |
34
+ | `NETLIFY_RECORDER_SECRET` | Secret string for access control — restricts who can view and act on your captured requests | Set in Netlify site environment variables or via `set-branch-secret` |
34
35
 
35
- **These are required.** `finishRequest` will throw an error if any are missing. Your deploy script should resolve them from git and set them on the Netlify site before deploying. Example:
36
+ The first three are **required** `finishRequest` will throw an error if any are missing. `NETLIFY_RECORDER_SECRET` is strongly recommended to prevent other apps from accessing your captured request data. Your deploy script should resolve the git values and set them on the Netlify site before deploying. Example:
36
37
 
37
38
  ```typescript
38
39
  // In your deploy script:
@@ -46,7 +47,7 @@ const repositoryUrl = execSync("git remote get-url origin", { encoding: "utf-8"
46
47
 
47
48
  ### 2. Wrap your Netlify function
48
49
 
49
- Use `createRecordingRequestHandler` with `remoteCallbacks()` to wrap your handler with automatic request capture.
50
+ Use `createRecordingRequestHandler` with `remoteCallbacks()` to wrap your handler with automatic request capture. Set `secret` to restrict access to captured requests — only API calls providing the same secret can view or act on them.
50
51
 
51
52
  **v1 handler** (Netlify Functions v1 — `event` with `httpMethod`, `path`, etc.):
52
53
 
@@ -71,6 +72,7 @@ const handler = createRecordingRequestHandler(
71
72
  {
72
73
  callbacks: remoteCallbacks(RECORDER_URL),
73
74
  handlerPath: "netlify/functions/my-handler",
75
+ secret: process.env.NETLIFY_RECORDER_SECRET,
74
76
  }
75
77
  );
76
78
 
@@ -102,6 +104,7 @@ export default createRecordingRequestHandler(
102
104
  {
103
105
  callbacks: remoteCallbacks(RECORDER_URL),
104
106
  handlerPath: "netlify/functions/my-handler",
107
+ secret: process.env.NETLIFY_RECORDER_SECRET,
105
108
  }
106
109
  );
107
110
  ```
@@ -112,7 +115,7 @@ export default createRecordingRequestHandler(
112
115
 
113
116
  ### 2. Create recordings
114
117
 
115
- When you want to turn a captured request into a Replay recording, POST to the service's `create-recording` endpoint with the request ID:
118
+ When you want to turn a captured request into a Replay recording, POST to the service's `create-recording` endpoint with the request ID. If the request was created with a secret, you must include it:
116
119
 
117
120
  ```typescript
118
121
  const response = await fetch(
@@ -122,6 +125,7 @@ const response = await fetch(
122
125
  headers: { "Content-Type": "application/json" },
123
126
  body: JSON.stringify({
124
127
  requestId,
128
+ secret,
125
129
  webhookUrl: "https://your-app.netlify.app/api/on-recording-complete", // optional
126
130
  }),
127
131
  }
@@ -144,17 +148,19 @@ On failure:
144
148
 
145
149
  ### 3. Check recording status
146
150
 
147
- You can poll the recording status at any time:
151
+ You can poll the recording status at any time. If the request was created with a secret, you must include it:
148
152
 
149
153
  ```typescript
150
154
  const res = await fetch(
151
- `${RECORDER_URL}/api/get-request?requestId=${requestId}`
155
+ `${RECORDER_URL}/api/get-request?requestId=${requestId}&secret=${secret}`
152
156
  );
153
157
  const { status, recordingId } = await res.json();
154
158
  // status: "captured" | "processing" | "recorded" | "failed"
155
159
  // recordingId: string | null
156
160
  ```
157
161
 
162
+ Requests created with a secret return 403 if the secret is missing or incorrect.
163
+
158
164
  ### 4. Access control with secrets
159
165
 
160
166
  You can restrict access to captured requests by setting a `secret` string. When a secret is set, the request is only accessible via API calls that provide the same secret value. This lets each app isolate its requests from other apps sharing the same Netlify Recorder service.
@@ -179,11 +185,11 @@ export default createRecordingRequestHandler(
179
185
 
180
186
  #### Listing requests by secret
181
187
 
182
- Use the `list-by-secret` endpoint to retrieve all requests associated with a secret, with optional filtering by status, handler path, and time range:
188
+ Use the `requests` endpoint with a `secret` parameter to retrieve all requests associated with your secret, with optional filtering by status, handler path, and time range:
183
189
 
184
190
  ```typescript
185
191
  const res = await fetch(
186
- `${RECORDER_URL}/api/list-by-secret?secret=${secret}&status=recorded&handlerPath=netlify/functions/my-handler&after=2025-01-01T00:00:00Z&before=2025-12-31T23:59:59Z`
192
+ `${RECORDER_URL}/api/requests?secret=${secret}&status=recorded&handlerPath=netlify/functions/my-handler&after=2025-01-01T00:00:00Z&before=2025-12-31T23:59:59Z`
187
193
  );
188
194
  const { rows, total, page, limit } = await res.json();
189
195
  ```
@@ -193,13 +199,16 @@ Query parameters:
193
199
  | Parameter | Description |
194
200
  |---|---|
195
201
  | `secret` | **(required)** The secret string used when creating the requests |
196
- | `status` | Filter by status: `captured`, `queued`, `processing`, `recorded`, `failed` |
202
+ | `id` | Search by request ID prefix |
203
+ | `status` | Filter by status: `captured`, `queued`, `processing`, `recorded`, `failed`, `all` |
197
204
  | `handlerPath` | Filter by handler path (exact match) |
198
205
  | `after` | Only include requests created at or after this ISO timestamp |
199
206
  | `before` | Only include requests created at or before this ISO timestamp |
200
207
  | `page` | Page number (default 1) |
201
208
  | `limit` | Page size (default 20, max 100) |
202
209
 
210
+ Without a `secret` parameter, only requests created without a secret are returned.
211
+
203
212
  #### Checking request status with a secret
204
213
 
205
214
  When a request was created with a secret, you must include the secret when polling its status:
@@ -256,6 +265,7 @@ const handler = createRecordingRequestHandler(
256
265
  };
257
266
  },
258
267
  {
268
+ secret: process.env.NETLIFY_RECORDER_SECRET,
259
269
  callbacks: {
260
270
  uploadBlob: async (data) => {
261
271
  // Upload the JSON string to your blob storage (S3, R2, etc.)
@@ -266,10 +276,10 @@ const handler = createRecordingRequestHandler(
266
276
  const { url } = await res.json();
267
277
  return url;
268
278
  },
269
- storeRequestData: async ({ blobUrl, commitSha, branchName, repositoryUrl, handlerPath }) => {
279
+ storeRequestData: async ({ blobUrl, commitSha, branchName, repositoryUrl, handlerPath, secret }) => {
270
280
  const [row] = await sql`
271
- INSERT INTO requests (blob_url, commit_sha, branch_name, repository_url, handler_path, status)
272
- VALUES (${blobUrl}, ${commitSha}, ${branchName}, ${repositoryUrl}, ${handlerPath}, 'captured')
281
+ INSERT INTO requests (blob_url, commit_sha, branch_name, repository_url, handler_path, secret, status)
282
+ VALUES (${blobUrl}, ${commitSha}, ${branchName}, ${repositoryUrl}, ${handlerPath}, ${secret}, 'captured')
273
283
  RETURNING id
274
284
  `;
275
285
  return row.id;
@@ -291,12 +301,15 @@ CREATE TABLE IF NOT EXISTS requests (
291
301
  branch_name TEXT,
292
302
  repository_url TEXT,
293
303
  handler_path TEXT,
304
+ secret TEXT,
294
305
  recording_id TEXT,
295
306
  status TEXT NOT NULL DEFAULT 'captured'
296
307
  CHECK (status IN ('captured', 'processing', 'recorded', 'failed')),
297
308
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
298
309
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
299
310
  );
311
+
312
+ CREATE INDEX IF NOT EXISTS idx_requests_secret ON requests (secret) WHERE secret IS NOT NULL;
300
313
  ```
301
314
 
302
315
  ### 4. Create a background function to produce recordings
@@ -428,6 +441,7 @@ export default createRecordingRequestHandler(
428
441
  {
429
442
  callbacks: remoteCallbacks(RECORDER_URL),
430
443
  handlerPath: "netlify/functions/create-order",
444
+ secret: process.env.NETLIFY_RECORDER_SECRET,
431
445
  }
432
446
  );
433
447
  ```
@@ -589,6 +603,7 @@ These must be set on your Netlify site. Your deploy script should resolve them f
589
603
  | `COMMIT_SHA` | Git commit hash of the deployed code | `git rev-parse HEAD` |
590
604
  | `BRANCH_NAME` | Git branch of the deployed code | `git rev-parse --abbrev-ref HEAD` |
591
605
  | `REPLAY_REPOSITORY_URL` | Git repository URL (no embedded credentials) | `git remote get-url origin` (strip tokens) |
606
+ | `NETLIFY_RECORDER_SECRET` | Secret for access control (strongly recommended) | `openssl rand -base64 32` — store in Netlify site env vars |
592
607
 
593
608
  ### Required for self-hosted recording (Option B)
594
609
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replayio-app-building/netlify-recorder",
3
- "version": "0.15.4",
3
+ "version": "0.15.6",
4
4
  "description": "Capture and replay Netlify function executions as Replay recordings",
5
5
  "type": "module",
6
6
  "exports": {