test-proxy-recorder 0.3.5 → 0.3.8
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 +295 -100
- package/dist/{index-BlBWqSE4.d.cts → index-BloXCw69.d.cts} +4 -0
- package/dist/{index-BlBWqSE4.d.ts → index-BloXCw69.d.ts} +4 -0
- package/dist/index.cjs +488 -400
- package/dist/index.d.cts +10 -42
- package/dist/index.d.ts +10 -42
- package/dist/index.mjs +486 -398
- package/dist/playwright/index.cjs +8 -0
- package/dist/playwright/index.d.cts +1 -1
- package/dist/playwright/index.d.ts +1 -1
- package/dist/playwright/index.mjs +8 -0
- package/dist/proxy.js +478 -398
- package/package.json +13 -17
- package/skills/nextjs-ssr/SKILL.md +377 -0
- package/skills/proxy-setup/SKILL.md +458 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "test-proxy-recorder",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "HTTP proxy server for recording and replaying network requests in testing. Works seamlessly with Playwright testing framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -35,28 +35,22 @@
|
|
|
35
35
|
"!dist/**/*.test.*",
|
|
36
36
|
"!dist/**/*.integration.test.*",
|
|
37
37
|
"README.md",
|
|
38
|
-
"LICENSE"
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"skills"
|
|
39
40
|
],
|
|
40
|
-
"pnpm": {
|
|
41
|
-
"onlyBuiltDependencies": ["esbuild", "sharp"]
|
|
42
|
-
},
|
|
43
|
-
"packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd",
|
|
44
41
|
"scripts": {
|
|
45
|
-
"start": "node dist/proxy.js",
|
|
46
42
|
"dev": "tsx src/proxy.ts",
|
|
47
43
|
"build": "tsup",
|
|
48
|
-
"example:dev": "pnpm --filter example-nextjs16 dev",
|
|
49
|
-
"example:build": "pnpm --filter example-nextjs16 build",
|
|
50
|
-
"example:services": "pnpm build && pnpm --filter example-nextjs16 start:all",
|
|
51
|
-
"example:test:e2e": "pnpm --filter example-nextjs16 test:e2e",
|
|
52
|
-
"example:test:e2e:record": "pnpm --filter example-nextjs16 test:e2e:record",
|
|
53
|
-
"prepublish": "pnpm run build && pnpm run test:run && pnpm run lint",
|
|
54
44
|
"lint": "eslint src --ext .ts",
|
|
55
45
|
"lint:fix": "eslint src --ext .ts --fix",
|
|
56
46
|
"typecheck": "tsc --noEmit",
|
|
57
47
|
"test": "vitest",
|
|
58
48
|
"test:run": "vitest run",
|
|
59
|
-
"test:coverage": "vitest run --coverage"
|
|
49
|
+
"test:coverage": "vitest run --coverage",
|
|
50
|
+
"prepare": "tsup",
|
|
51
|
+
"sync-readme": "cp ../../README.md README.md",
|
|
52
|
+
"prepack": "pnpm sync-readme",
|
|
53
|
+
"postpack": "rm -f README.md"
|
|
60
54
|
},
|
|
61
55
|
"keywords": [
|
|
62
56
|
"playwright",
|
|
@@ -76,7 +70,8 @@
|
|
|
76
70
|
"integration-testing",
|
|
77
71
|
"test-automation",
|
|
78
72
|
"api-mocking",
|
|
79
|
-
"network-recording"
|
|
73
|
+
"network-recording",
|
|
74
|
+
"tanstack-intent"
|
|
80
75
|
],
|
|
81
76
|
"author": "asmyshlyaev177",
|
|
82
77
|
"license": "MIT",
|
|
@@ -89,7 +84,7 @@
|
|
|
89
84
|
},
|
|
90
85
|
"homepage": "https://github.com/asmyshlyaev177/test-proxy-recorder#readme",
|
|
91
86
|
"engines": {
|
|
92
|
-
"node": ">=
|
|
87
|
+
"node": ">=20.0.0"
|
|
93
88
|
},
|
|
94
89
|
"dependencies": {
|
|
95
90
|
"commander": "^12.0.0",
|
|
@@ -101,8 +96,9 @@
|
|
|
101
96
|
},
|
|
102
97
|
"devDependencies": {
|
|
103
98
|
"@playwright/test": "^1.59.1",
|
|
99
|
+
"@tanstack/intent": "^0.0.41",
|
|
104
100
|
"@types/http-proxy": "^1.17.15",
|
|
105
|
-
"@types/node": "^
|
|
101
|
+
"@types/node": "^20.0.0",
|
|
106
102
|
"@types/ws": "^8.18.1",
|
|
107
103
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
108
104
|
"@typescript-eslint/parser": "^8.0.0",
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nextjs-ssr
|
|
3
|
+
description: >
|
|
4
|
+
Wire the x-test-rcrd-id session header through Next.js so server-side fetches
|
|
5
|
+
are recorded under the correct Playwright test session. Covers
|
|
6
|
+
setNextProxyHeaders, createHeadersWithRecordingId, getRecordingId,
|
|
7
|
+
RECORDING_ID_HEADER, middleware.ts (Next.js 13–15), proxy.ts (Next.js 16),
|
|
8
|
+
the React cache() memoization pattern for next/headers, and the axios
|
|
9
|
+
interceptor pattern for SSR requests. Load this skill when setting up
|
|
10
|
+
test-proxy-recorder in a Next.js app that makes server-side API calls.
|
|
11
|
+
type: framework
|
|
12
|
+
library: test-proxy-recorder
|
|
13
|
+
framework: nextjs
|
|
14
|
+
library_version: "0.3.5"
|
|
15
|
+
requires:
|
|
16
|
+
- test-proxy-recorder/proxy-setup
|
|
17
|
+
sources:
|
|
18
|
+
- "asmyshlyaev177/test-proxy-recorder:README.md"
|
|
19
|
+
- "asmyshlyaev177/test-proxy-recorder:packages/test-proxy-recorder/src/nextjs/middleware.ts"
|
|
20
|
+
- "asmyshlyaev177/test-proxy-recorder:apps/example-nextjs16"
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
This skill builds on test-proxy-recorder/proxy-setup. Read it first for proxy
|
|
24
|
+
CLI setup, playwright.config.ts, and fixtures before applying Next.js patterns.
|
|
25
|
+
|
|
26
|
+
# test-proxy-recorder — Next.js SSR
|
|
27
|
+
|
|
28
|
+
The proxy correlates SSR fetches to the right test session via
|
|
29
|
+
`x-test-rcrd-id`. Playwright sets this header on the browser page automatically
|
|
30
|
+
via `playwrightProxy.before()`. For server-side fetches (SSR, Server
|
|
31
|
+
Components, Route Handlers), the header must be explicitly forwarded through
|
|
32
|
+
every layer.
|
|
33
|
+
|
|
34
|
+
All helpers from `test-proxy-recorder/nextjs` are **no-ops in production**
|
|
35
|
+
(`NODE_ENV=production`) unless `TEST_PROXY_RECORDER_ENABLED=true` is set.
|
|
36
|
+
|
|
37
|
+
## Setup
|
|
38
|
+
|
|
39
|
+
### Next.js 13–15 — middleware.ts
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
// middleware.ts (Next.js 13–15 — at project root)
|
|
43
|
+
import { NextResponse } from 'next/server';
|
|
44
|
+
import type { NextRequest } from 'next/server';
|
|
45
|
+
import { setNextProxyHeaders } from 'test-proxy-recorder/nextjs';
|
|
46
|
+
|
|
47
|
+
export function middleware(request: NextRequest) {
|
|
48
|
+
const response = NextResponse.next();
|
|
49
|
+
setNextProxyHeaders(request, response); // no-op in production
|
|
50
|
+
return response;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const config = {
|
|
54
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
55
|
+
};
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Next.js 16 — proxy.ts
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// proxy.ts (Next.js 16 — at project root, alongside next.config.ts)
|
|
62
|
+
import { NextResponse } from 'next/server';
|
|
63
|
+
import type { NextRequest } from 'next/server';
|
|
64
|
+
import { setNextProxyHeaders } from 'test-proxy-recorder/nextjs';
|
|
65
|
+
|
|
66
|
+
export function proxy(request: NextRequest) {
|
|
67
|
+
const response = NextResponse.next();
|
|
68
|
+
setNextProxyHeaders(request, response);
|
|
69
|
+
return response;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const config = {
|
|
73
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Core Patterns
|
|
78
|
+
|
|
79
|
+
### Inject recording ID into native fetch (Route Handler / Server Component)
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// app/api/data/route.ts
|
|
83
|
+
import { headers } from 'next/headers';
|
|
84
|
+
import { createHeadersWithRecordingId } from 'test-proxy-recorder/nextjs';
|
|
85
|
+
|
|
86
|
+
export async function GET(request: Request) {
|
|
87
|
+
const res = await fetch('http://localhost:8100/api/data', {
|
|
88
|
+
cache: 'no-store',
|
|
89
|
+
headers: createHeadersWithRecordingId(await headers(), {
|
|
90
|
+
'Content-Type': 'application/json',
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
return Response.json(await res.json());
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`createHeadersWithRecordingId` merges the session ID into your headers object.
|
|
98
|
+
It is a no-op when the session ID is absent (browser-only tests, or production).
|
|
99
|
+
|
|
100
|
+
### Memoize header lookup with React cache() (App Router)
|
|
101
|
+
|
|
102
|
+
Avoid calling `headers()` in every individual fetch helper. Wrap it once with
|
|
103
|
+
`React.cache()` so it is called once per request and shared across all
|
|
104
|
+
server-side imports.
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// lib/recording-id.ts
|
|
108
|
+
import { cache } from 'react';
|
|
109
|
+
import { RECORDING_ID_HEADER } from 'test-proxy-recorder/nextjs';
|
|
110
|
+
|
|
111
|
+
export const getServerRecordingId = cache(async () => {
|
|
112
|
+
try {
|
|
113
|
+
const { headers } = await import('next/headers');
|
|
114
|
+
const headersList = await headers();
|
|
115
|
+
return headersList.get(RECORDING_ID_HEADER.toLowerCase()) || undefined;
|
|
116
|
+
} catch {
|
|
117
|
+
return undefined; // Not in a Server Component request context
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Axios interceptor for SSR requests
|
|
123
|
+
|
|
124
|
+
Use this pattern when your app uses axios for server-side API calls instead of
|
|
125
|
+
native `fetch`.
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// lib/axios-server.ts
|
|
129
|
+
import axios from 'axios';
|
|
130
|
+
import { RECORDING_ID_HEADER } from 'test-proxy-recorder/nextjs';
|
|
131
|
+
import { getServerRecordingId } from './recording-id';
|
|
132
|
+
|
|
133
|
+
const isTestMode = process.env.NODE_ENV !== 'production';
|
|
134
|
+
|
|
135
|
+
export const axiosForServer = axios.create();
|
|
136
|
+
|
|
137
|
+
axiosForServer.interceptors.request.use(async (config) => {
|
|
138
|
+
if (typeof window === 'undefined' && isTestMode) {
|
|
139
|
+
try {
|
|
140
|
+
const recordingId = await getServerRecordingId();
|
|
141
|
+
if (recordingId) {
|
|
142
|
+
config.headers.set(RECORDING_ID_HEADER, recordingId);
|
|
143
|
+
}
|
|
144
|
+
} catch {
|
|
145
|
+
// Not in a Server Component context — silently skip
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return config;
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Extract recording ID manually
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { getRecordingId, RECORDING_ID_HEADER } from 'test-proxy-recorder/nextjs';
|
|
156
|
+
import { headers } from 'next/headers';
|
|
157
|
+
|
|
158
|
+
// From headers() in a Server Component
|
|
159
|
+
const recordingId = getRecordingId(await headers());
|
|
160
|
+
|
|
161
|
+
// From NextRequest in middleware
|
|
162
|
+
const recordingId = getRecordingId(request.headers);
|
|
163
|
+
|
|
164
|
+
// Forward manually
|
|
165
|
+
if (recordingId) {
|
|
166
|
+
requestHeaders.set(RECORDING_ID_HEADER, recordingId);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Common Mistakes
|
|
171
|
+
|
|
172
|
+
### HIGH Using middleware.ts (or a middleware export) in Next.js 16
|
|
173
|
+
|
|
174
|
+
Wrong:
|
|
175
|
+
```typescript
|
|
176
|
+
// middleware.ts — ignored in Next.js 16, session header never forwarded
|
|
177
|
+
import { setNextProxyHeaders } from 'test-proxy-recorder/nextjs';
|
|
178
|
+
export function middleware(request) {
|
|
179
|
+
const response = NextResponse.next();
|
|
180
|
+
setNextProxyHeaders(request, response);
|
|
181
|
+
return response;
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Correct:
|
|
186
|
+
```typescript
|
|
187
|
+
// proxy.ts — Next.js 16 middleware entry point, at project root
|
|
188
|
+
import { setNextProxyHeaders } from 'test-proxy-recorder/nextjs';
|
|
189
|
+
export function proxy(request) {
|
|
190
|
+
const response = NextResponse.next();
|
|
191
|
+
setNextProxyHeaders(request, response);
|
|
192
|
+
return response;
|
|
193
|
+
}
|
|
194
|
+
export const config = {
|
|
195
|
+
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
|
|
196
|
+
};
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Next.js 16 replaced `middleware.ts` with `proxy.ts` as the middleware entry
|
|
200
|
+
point, and the exported function is named `proxy`, not `middleware`. Keeping
|
|
201
|
+
either old name silently does nothing — the session header is never forwarded
|
|
202
|
+
and all SSR recordings are grouped under the wrong session.
|
|
203
|
+
|
|
204
|
+
Source: apps/example-nextjs16/proxy.ts
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### HIGH setNextProxyHeaders set but SSR fetches still missing the header
|
|
209
|
+
|
|
210
|
+
Wrong:
|
|
211
|
+
```typescript
|
|
212
|
+
// middleware.ts — header set on response, but individual fetch calls don't use it
|
|
213
|
+
export function middleware(request) {
|
|
214
|
+
const response = NextResponse.next();
|
|
215
|
+
setNextProxyHeaders(request, response);
|
|
216
|
+
return response;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// app/api/data/route.ts — header not forwarded to outgoing fetch
|
|
220
|
+
export async function GET() {
|
|
221
|
+
const data = await fetch('http://localhost:8100/api/data');
|
|
222
|
+
return Response.json(await data.json());
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Correct:
|
|
227
|
+
```typescript
|
|
228
|
+
// app/api/data/route.ts — explicitly inject header into each outgoing fetch
|
|
229
|
+
import { headers } from 'next/headers';
|
|
230
|
+
import { createHeadersWithRecordingId } from 'test-proxy-recorder/nextjs';
|
|
231
|
+
|
|
232
|
+
export async function GET() {
|
|
233
|
+
const data = await fetch('http://localhost:8100/api/data', {
|
|
234
|
+
headers: createHeadersWithRecordingId(await headers()),
|
|
235
|
+
});
|
|
236
|
+
return Response.json(await data.json());
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
`setNextProxyHeaders` makes the session ID available to server components via
|
|
241
|
+
`next/headers`. It does **not** automatically inject the header into outgoing
|
|
242
|
+
fetch calls — each server-side fetch must use `createHeadersWithRecordingId()`
|
|
243
|
+
explicitly.
|
|
244
|
+
|
|
245
|
+
Source: README.md — Manual header forwarding; channels/web/app/api
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
### HIGH Recording against a production build without TEST_PROXY_RECORDER_ENABLED
|
|
250
|
+
|
|
251
|
+
Wrong:
|
|
252
|
+
```json
|
|
253
|
+
{
|
|
254
|
+
"scripts": {
|
|
255
|
+
"start:proxy": "concurrently \"pnpm proxy\" \"INTERNAL_API_URL=http://localhost:8100 next start\""
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Correct:
|
|
261
|
+
```json
|
|
262
|
+
{
|
|
263
|
+
"scripts": {
|
|
264
|
+
"start:proxy": "concurrently \"pnpm proxy\" \"INTERNAL_API_URL=http://localhost:8100 TEST_PROXY_RECORDER_ENABLED=true next start\""
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Recording should run against a production build (`next build && next start` —
|
|
270
|
+
see proxy-setup), but `next build` sets `NODE_ENV=production`, which turns
|
|
271
|
+
`setNextProxyHeaders` and `createHeadersWithRecordingId` into silent no-ops.
|
|
272
|
+
SSR requests still flow through the proxy but lose their session ID, so they
|
|
273
|
+
are recorded under the wrong session — or not at all. Set
|
|
274
|
+
`TEST_PROXY_RECORDER_ENABLED=true` on the app process whenever testing a
|
|
275
|
+
production build.
|
|
276
|
+
|
|
277
|
+
Source: packages/test-proxy-recorder/src/nextjs/middleware.ts — isRecorderEnabled()
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
### MEDIUM Importing next/headers at module level in an axios interceptor
|
|
282
|
+
|
|
283
|
+
Wrong:
|
|
284
|
+
```typescript
|
|
285
|
+
import { headers } from 'next/headers'; // throws when evaluated on the client
|
|
286
|
+
|
|
287
|
+
axiosForServer.interceptors.request.use(async (config) => {
|
|
288
|
+
const id = (await headers()).get('x-test-rcrd-id');
|
|
289
|
+
config.headers.set('x-test-rcrd-id', id);
|
|
290
|
+
return config;
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Correct:
|
|
295
|
+
```typescript
|
|
296
|
+
axiosForServer.interceptors.request.use(async (config) => {
|
|
297
|
+
if (typeof window === 'undefined' && isTestMode) {
|
|
298
|
+
try {
|
|
299
|
+
const { getServerRecordingId } = await import('./recording-id');
|
|
300
|
+
const recordingId = await getServerRecordingId();
|
|
301
|
+
if (recordingId) config.headers.set(RECORDING_ID_HEADER, recordingId);
|
|
302
|
+
} catch {
|
|
303
|
+
// Not in a Server Component context — silently skip
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return config;
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`next/headers` throws when imported outside a Server Component request context
|
|
311
|
+
(including on the client). Always lazy-import it inside the interceptor, guard
|
|
312
|
+
with `typeof window === 'undefined'`, and wrap in try/catch.
|
|
313
|
+
|
|
314
|
+
Source: channels/web/core/api/axios.ts
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### MEDIUM Calling headers() on every SSR fetch instead of caching per request
|
|
319
|
+
|
|
320
|
+
Wrong:
|
|
321
|
+
```typescript
|
|
322
|
+
// Called independently in each server utility — redundant async header reads
|
|
323
|
+
async function fetchUsers() {
|
|
324
|
+
const { headers } = await import('next/headers');
|
|
325
|
+
const id = (await headers()).get('x-test-rcrd-id');
|
|
326
|
+
return fetch(url, { headers: { 'x-test-rcrd-id': id ?? '' } });
|
|
327
|
+
}
|
|
328
|
+
async function fetchPosts() {
|
|
329
|
+
const { headers } = await import('next/headers');
|
|
330
|
+
const id = (await headers()).get('x-test-rcrd-id');
|
|
331
|
+
return fetch(url2, { headers: { 'x-test-rcrd-id': id ?? '' } });
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
Correct:
|
|
336
|
+
```typescript
|
|
337
|
+
// lib/recording-id.ts — memoized once per request via React cache()
|
|
338
|
+
import { cache } from 'react';
|
|
339
|
+
import { RECORDING_ID_HEADER } from 'test-proxy-recorder/nextjs';
|
|
340
|
+
export const getServerRecordingId = cache(async () => { /* ... */ });
|
|
341
|
+
|
|
342
|
+
// Each utility reuses the cached value
|
|
343
|
+
async function fetchUsers() {
|
|
344
|
+
const id = await getServerRecordingId();
|
|
345
|
+
return fetch(url, { headers: id ? { [RECORDING_ID_HEADER]: id } : {} });
|
|
346
|
+
}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Wrap the `headers()` call in `React.cache()` once. The memoized function is
|
|
350
|
+
called once per server request regardless of how many fetch utilities invoke it.
|
|
351
|
+
|
|
352
|
+
Source: channels/web/lib/recording-id.ts
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### MEDIUM Manually setting header without production guard
|
|
357
|
+
|
|
358
|
+
Wrong:
|
|
359
|
+
```typescript
|
|
360
|
+
// No production guard — leaks session IDs in prod if env var is misconfigured
|
|
361
|
+
response.headers.set('x-test-rcrd-id', request.headers.get('x-test-rcrd-id') ?? '');
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Correct:
|
|
365
|
+
```typescript
|
|
366
|
+
import { setNextProxyHeaders } from 'test-proxy-recorder/nextjs';
|
|
367
|
+
setNextProxyHeaders(request, response); // automatically skips in production
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
Use the library helpers instead of manually reading/setting `x-test-rcrd-id`.
|
|
371
|
+
`setNextProxyHeaders` and `createHeadersWithRecordingId` both check
|
|
372
|
+
`NODE_ENV !== 'production'` (or `TEST_PROXY_RECORDER_ENABLED`) and are no-ops
|
|
373
|
+
when the guard fails.
|
|
374
|
+
|
|
375
|
+
Source: packages/test-proxy-recorder/src/nextjs/middleware.ts — isRecorderEnabled()
|
|
376
|
+
|
|
377
|
+
See also: test-proxy-recorder/proxy-setup — for proxy CLI, fixtures, and record/replay lifecycle
|