|
4 | 4 | // |
5 | 5 | // CAPTURE_FETCH_STUBS is a JSON object mapping a URL prefix to the absolute |
6 | 6 | // path of a JSON file served as the response body. |
| 7 | +// |
| 8 | +// CAPTURE_FETCH_LATENCY is a floor, in milliseconds, on how long every request |
| 9 | +// takes. A prompt spinner is only ever drawn from its 80ms repaint timer, so a |
| 10 | +// request that resolves sooner than that leaves no trace in the recording at |
| 11 | +// all: the floor keeps the spinner on screen whatever the link is doing. |
7 | 12 |
|
8 | 13 | import { readFileSync } from 'node:fs' |
9 | 14 |
|
10 | 15 | const stubs = Object.entries(JSON.parse(process.env.CAPTURE_FETCH_STUBS ?? '{}')) |
| 16 | +const latency = Number(process.env.CAPTURE_FETCH_LATENCY ?? 0) |
11 | 17 | const realFetch = globalThis.fetch |
12 | 18 |
|
13 | 19 | globalThis.fetch = async (input, init) => { |
14 | 20 | const url = typeof input === 'string' ? input : input.url ?? String(input) |
| 21 | + const settled = latency > 0 ? new Promise(resolve => setTimeout(resolve, latency)) : undefined |
15 | 22 | for (const [prefix, file] of stubs) { |
16 | 23 | if (url.startsWith(prefix)) { |
17 | | - return new Response(readFileSync(file, 'utf8'), { |
| 24 | + const body = readFileSync(file, 'utf8') |
| 25 | + await settled |
| 26 | + return new Response(body, { |
18 | 27 | status: 200, |
19 | 28 | headers: { 'content-type': 'application/json' }, |
20 | 29 | }) |
21 | 30 | } |
22 | 31 | } |
23 | | - return realFetch(input, init) |
| 32 | + const response = await realFetch(input, init) |
| 33 | + await settled |
| 34 | + return response |
24 | 35 | } |
0 commit comments