@noodleseed/one 0.22.0 → 0.23.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.
Files changed (197) hide show
  1. package/dist/commands/author-loop.d.ts.map +1 -1
  2. package/dist/commands/author-loop.js +2 -2
  3. package/dist/commands/author-loop.js.map +1 -1
  4. package/dist/commands/catalog-dispatch.d.ts.map +1 -1
  5. package/dist/commands/catalog-dispatch.js +21 -2
  6. package/dist/commands/catalog-dispatch.js.map +1 -1
  7. package/dist/commands/deploy-ops.d.ts.map +1 -1
  8. package/dist/commands/deploy-ops.js +28 -11
  9. package/dist/commands/deploy-ops.js.map +1 -1
  10. package/dist/first-run.d.ts +11 -0
  11. package/dist/first-run.d.ts.map +1 -1
  12. package/dist/first-run.js +19 -4
  13. package/dist/first-run.js.map +1 -1
  14. package/dist/project-scaffold-templates.d.ts +11 -0
  15. package/dist/project-scaffold-templates.d.ts.map +1 -0
  16. package/dist/project-scaffold-templates.js +359 -0
  17. package/dist/project-scaffold-templates.js.map +1 -0
  18. package/dist/project.d.ts.map +1 -1
  19. package/dist/project.js +1 -323
  20. package/dist/project.js.map +1 -1
  21. package/dist/react.d.ts +2 -2
  22. package/dist/react.d.ts.map +1 -1
  23. package/dist/react.js +1 -1
  24. package/dist/react.js.map +1 -1
  25. package/dist/validate.d.ts +12 -0
  26. package/dist/validate.d.ts.map +1 -1
  27. package/dist/validate.js +11 -0
  28. package/dist/validate.js.map +1 -1
  29. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.d.ts.map +1 -1
  30. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js +6 -9
  31. package/node_modules/@noodle-borg/agent-kit/dist/skill-content.js.map +1 -1
  32. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  33. package/node_modules/@noodle-borg/authoring/dist/index.d.ts +3 -3
  34. package/node_modules/@noodle-borg/authoring/dist/index.d.ts.map +1 -1
  35. package/node_modules/@noodle-borg/authoring/dist/index.js +1 -1
  36. package/node_modules/@noodle-borg/authoring/dist/index.js.map +1 -1
  37. package/node_modules/@noodle-borg/authoring/dist/react/components.d.ts +302 -7
  38. package/node_modules/@noodle-borg/authoring/dist/react/components.d.ts.map +1 -1
  39. package/node_modules/@noodle-borg/authoring/dist/react/components.js +39 -7
  40. package/node_modules/@noodle-borg/authoring/dist/react/components.js.map +1 -1
  41. package/node_modules/@noodle-borg/authoring/dist/react/index.d.ts +4 -2
  42. package/node_modules/@noodle-borg/authoring/dist/react/index.d.ts.map +1 -1
  43. package/node_modules/@noodle-borg/authoring/dist/react/index.js +2 -1
  44. package/node_modules/@noodle-borg/authoring/dist/react/index.js.map +1 -1
  45. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.d.ts +2618 -0
  46. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.d.ts.map +1 -0
  47. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.js +110 -0
  48. package/node_modules/@noodle-borg/authoring/dist/react/semantic-components.js.map +1 -0
  49. package/node_modules/@noodle-borg/authoring/dist/react.d.ts +2 -2
  50. package/node_modules/@noodle-borg/authoring/dist/react.d.ts.map +1 -1
  51. package/node_modules/@noodle-borg/authoring/dist/react.js +1 -1
  52. package/node_modules/@noodle-borg/authoring/dist/react.js.map +1 -1
  53. package/node_modules/@noodle-borg/authoring/dist/recording.d.ts +1 -1
  54. package/node_modules/@noodle-borg/authoring/dist/recording.d.ts.map +1 -1
  55. package/node_modules/@noodle-borg/authoring/dist/recording.js +14 -1
  56. package/node_modules/@noodle-borg/authoring/dist/recording.js.map +1 -1
  57. package/node_modules/@noodle-borg/authoring/dist/server.d.ts +25 -2
  58. package/node_modules/@noodle-borg/authoring/dist/server.d.ts.map +1 -1
  59. package/node_modules/@noodle-borg/authoring/dist/server.js +7 -3
  60. package/node_modules/@noodle-borg/authoring/dist/server.js.map +1 -1
  61. package/node_modules/@noodle-borg/compiler/dist/compile.d.ts.map +1 -1
  62. package/node_modules/@noodle-borg/compiler/dist/compile.js +20 -8
  63. package/node_modules/@noodle-borg/compiler/dist/compile.js.map +1 -1
  64. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts +2 -0
  65. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.d.ts.map +1 -1
  66. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js +6 -0
  67. package/node_modules/@noodle-borg/compiler/dist/manifest/schema.js.map +1 -1
  68. package/node_modules/@noodle-borg/connector-http/package.json +1 -1
  69. package/node_modules/@noodle-borg/protocol/dist/mapping.d.ts.map +1 -1
  70. package/node_modules/@noodle-borg/protocol/dist/mapping.js +8 -0
  71. package/node_modules/@noodle-borg/protocol/dist/mapping.js.map +1 -1
  72. package/node_modules/hono/dist/adapter/aws-lambda/handler.js +15 -21
  73. package/node_modules/hono/dist/adapter/bun/websocket.js +3 -1
  74. package/node_modules/hono/dist/adapter/lambda-edge/handler.js +8 -2
  75. package/node_modules/hono/dist/cjs/adapter/aws-lambda/handler.js +15 -21
  76. package/node_modules/hono/dist/cjs/adapter/bun/websocket.js +3 -1
  77. package/node_modules/hono/dist/cjs/adapter/lambda-edge/handler.js +8 -2
  78. package/node_modules/hono/dist/cjs/helper/css/common.js +3 -1
  79. package/node_modules/hono/dist/cjs/helper/css/index.js +9 -1
  80. package/node_modules/hono/dist/cjs/jsx/base.js +8 -14
  81. package/node_modules/hono/dist/cjs/jsx/components.js +41 -21
  82. package/node_modules/hono/dist/cjs/jsx/context.js +131 -5
  83. package/node_modules/hono/dist/cjs/jsx/streaming.js +9 -7
  84. package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -5
  85. package/node_modules/hono/dist/cjs/middleware/serve-static/index.js +2 -2
  86. package/node_modules/hono/dist/cjs/utils/body.js +12 -4
  87. package/node_modules/hono/dist/cjs/utils/buffer.js +2 -1
  88. package/node_modules/hono/dist/cjs/validator/validator.js +3 -3
  89. package/node_modules/hono/dist/helper/css/common.js +3 -1
  90. package/node_modules/hono/dist/helper/css/index.js +9 -1
  91. package/node_modules/hono/dist/jsx/base.js +15 -15
  92. package/node_modules/hono/dist/jsx/components.js +42 -22
  93. package/node_modules/hono/dist/jsx/context.js +129 -5
  94. package/node_modules/hono/dist/jsx/streaming.js +10 -8
  95. package/node_modules/hono/dist/middleware/cors/index.js +2 -5
  96. package/node_modules/hono/dist/middleware/serve-static/index.js +2 -2
  97. package/node_modules/hono/dist/types/adapter/aws-lambda/handler.d.ts +1 -1
  98. package/node_modules/hono/dist/types/jsx/base.d.ts +1 -3
  99. package/node_modules/hono/dist/types/jsx/context.d.ts +39 -0
  100. package/node_modules/hono/dist/types/middleware/context-storage/index.d.ts +2 -2
  101. package/node_modules/hono/dist/types/utils/body.d.ts +1 -1
  102. package/node_modules/hono/dist/utils/body.js +12 -4
  103. package/node_modules/hono/dist/utils/buffer.js +2 -1
  104. package/node_modules/hono/dist/validator/validator.js +3 -3
  105. package/node_modules/hono/package.json +9 -10
  106. package/node_modules/tailwindcss/dist/chunk-HMCCH6MG.mjs +1 -0
  107. package/node_modules/tailwindcss/dist/default-theme.mjs +1 -1
  108. package/node_modules/tailwindcss/dist/lib.js +13 -13
  109. package/node_modules/tailwindcss/dist/lib.mjs +18 -18
  110. package/node_modules/tailwindcss/package.json +2 -2
  111. package/node_modules/undici/README.md +3 -2
  112. package/node_modules/undici/docs/docs/api/Agent.md +248 -44
  113. package/node_modules/undici/docs/docs/api/BalancedPool.md +246 -48
  114. package/node_modules/undici/docs/docs/api/CacheStorage.md +171 -13
  115. package/node_modules/undici/docs/docs/api/CacheStore.md +294 -98
  116. package/node_modules/undici/docs/docs/api/Client.md +365 -178
  117. package/node_modules/undici/docs/docs/api/ClientStats.md +80 -14
  118. package/node_modules/undici/docs/docs/api/Connector.md +118 -36
  119. package/node_modules/undici/docs/docs/api/ContentType.md +65 -25
  120. package/node_modules/undici/docs/docs/api/Cookies.md +125 -47
  121. package/node_modules/undici/docs/docs/api/Debug.md +34 -18
  122. package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +364 -164
  123. package/node_modules/undici/docs/docs/api/Dispatcher.md +479 -1093
  124. package/node_modules/undici/docs/docs/api/EnvHttpProxyAgent.md +101 -87
  125. package/node_modules/undici/docs/docs/api/Errors.md +503 -40
  126. package/node_modules/undici/docs/docs/api/EventSource.md +294 -32
  127. package/node_modules/undici/docs/docs/api/Fetch.md +680 -31
  128. package/node_modules/undici/docs/docs/api/GlobalInstallation.md +78 -98
  129. package/node_modules/undici/docs/docs/api/H2CClient.md +298 -176
  130. package/node_modules/undici/docs/docs/api/Interceptors.md +355 -0
  131. package/node_modules/undici/docs/docs/api/MockAgent.md +314 -322
  132. package/node_modules/undici/docs/docs/api/MockCallHistory.md +272 -98
  133. package/node_modules/undici/docs/docs/api/MockCallHistoryLog.md +189 -26
  134. package/node_modules/undici/docs/docs/api/MockClient.md +151 -33
  135. package/node_modules/undici/docs/docs/api/MockErrors.md +56 -5
  136. package/node_modules/undici/docs/docs/api/MockPool.md +299 -263
  137. package/node_modules/undici/docs/docs/api/Pool.md +235 -43
  138. package/node_modules/undici/docs/docs/api/PoolStats.md +119 -13
  139. package/node_modules/undici/docs/docs/api/ProxyAgent.md +179 -160
  140. package/node_modules/undici/docs/docs/api/RedirectHandler.md +238 -63
  141. package/node_modules/undici/docs/docs/api/RetryAgent.md +113 -26
  142. package/node_modules/undici/docs/docs/api/RetryHandler.md +161 -77
  143. package/node_modules/undici/docs/docs/api/RoundRobinPool.md +166 -72
  144. package/node_modules/undici/docs/docs/api/SnapshotAgent.md +264 -542
  145. package/node_modules/undici/docs/docs/api/Socks5ProxyAgent.md +162 -186
  146. package/node_modules/undici/docs/docs/api/Util.md +68 -11
  147. package/node_modules/undici/docs/docs/api/WebSocket.md +475 -80
  148. package/node_modules/undici/docs/docs/api/api-lifecycle.md +100 -32
  149. package/node_modules/undici/docs/docs/best-practices/client-certificate.md +2 -0
  150. package/node_modules/undici/docs/docs/best-practices/crawling.md +3 -1
  151. package/node_modules/undici/docs/docs/best-practices/migrating-from-v7-to-v8.md +6 -4
  152. package/node_modules/undici/docs/docs/best-practices/mocking-request.md +9 -7
  153. package/node_modules/undici/docs/docs/best-practices/proxy.md +3 -1
  154. package/node_modules/undici/docs/docs/best-practices/undici-vs-builtin-fetch.md +10 -8
  155. package/node_modules/undici/docs/docs/best-practices/writing-tests.md +2 -0
  156. package/node_modules/undici/docs/docs/{GettingStarted.md → getting-started.md} +22 -19
  157. package/node_modules/undici/docs/docs/index.md +779 -0
  158. package/node_modules/undici/docs/docs/site.json +125 -0
  159. package/node_modules/undici/docs/docs/type-map.json +79 -0
  160. package/node_modules/undici/lib/api/api-request.js +7 -1
  161. package/node_modules/undici/lib/api/readable.js +47 -2
  162. package/node_modules/undici/lib/core/errors.js +20 -0
  163. package/node_modules/undici/lib/core/request.js +1 -1
  164. package/node_modules/undici/lib/core/util.js +24 -1
  165. package/node_modules/undici/lib/dispatcher/client-h1.js +92 -1
  166. package/node_modules/undici/lib/dispatcher/client-h2.js +322 -118
  167. package/node_modules/undici/lib/dispatcher/client.js +6 -2
  168. package/node_modules/undici/lib/dispatcher/dispatcher-base.js +1 -0
  169. package/node_modules/undici/lib/dispatcher/proxy-agent.js +42 -7
  170. package/node_modules/undici/lib/dispatcher/socks5-proxy-agent.js +4 -2
  171. package/node_modules/undici/lib/handler/redirect-handler.js +1 -0
  172. package/node_modules/undici/lib/handler/retry-handler.js +57 -19
  173. package/node_modules/undici/lib/util/cache.js +8 -2
  174. package/node_modules/undici/lib/web/cookies/parse.js +20 -27
  175. package/node_modules/undici/lib/web/cookies/util.js +1 -1
  176. package/node_modules/undici/lib/web/eventsource/eventsource.js +7 -18
  177. package/node_modules/undici/lib/web/eventsource/util.js +32 -1
  178. package/node_modules/undici/lib/web/fetch/body.js +43 -0
  179. package/node_modules/undici/lib/web/fetch/constants.js +1 -1
  180. package/node_modules/undici/lib/web/fetch/index.js +11 -2
  181. package/node_modules/undici/lib/web/fetch/request.js +1 -0
  182. package/node_modules/undici/lib/web/fetch/util.js +4 -1
  183. package/node_modules/undici/lib/web/websocket/receiver.js +20 -3
  184. package/node_modules/undici/lib/web/websocket/stream/websocketstream.js +8 -1
  185. package/node_modules/undici/lib/web/websocket/websocket.js +3 -1
  186. package/node_modules/undici/package.json +1 -1
  187. package/node_modules/undici/types/client.d.ts +6 -1
  188. package/node_modules/undici/types/cookies.d.ts +1 -1
  189. package/node_modules/undici/types/errors.d.ts +10 -0
  190. package/node_modules/undici/types/fetch.d.ts +1 -0
  191. package/node_modules/undici/types/handlers.d.ts +2 -0
  192. package/node_modules/undici/types/proxy-agent.d.ts +7 -0
  193. package/package.json +4 -4
  194. package/react/styles.css +510 -5
  195. package/react/styles.source.css +850 -0
  196. package/node_modules/hono/dist/tsconfig.build.tsbuildinfo +0 -1
  197. package/node_modules/tailwindcss/dist/chunk-XVOTX5KS.mjs +0 -1
@@ -1,639 +1,361 @@
1
1
  # SnapshotAgent
2
2
 
3
- The `SnapshotAgent` provides a powerful way to record and replay HTTP requests for testing purposes. It extends `MockAgent` to enable automatic snapshot testing, eliminating the need to manually define mock responses.
3
+ <!--introduced_in=v7.13.0-->
4
+ <!--type=module-->
5
+ <!-- source_link=lib/mock/snapshot-agent.js -->
4
6
 
5
- ## Use Cases
7
+ > Stability: 1 - Experimental
6
8
 
7
- - **Integration Testing**: Record real API interactions and replay them in tests
8
- - **Offline Development**: Work with APIs without network connectivity
9
- - **Consistent Test Data**: Ensure tests use the same responses across runs
10
- - **API Contract Testing**: Capture and validate API behavior over time
9
+ `SnapshotAgent` records real HTTP responses and replays them on later requests,
10
+ allowing tests to run against deterministic, captured data instead of a live
11
+ network. It extends [`MockAgent`][], so it can be installed with
12
+ [`setGlobalDispatcher()`][] or passed explicitly through the `dispatcher`
13
+ option, and it works with every undici API (`fetch`, `request`, `stream`,
14
+ `pipeline`, and so on).
11
15
 
12
- ## Constructor
16
+ A `SnapshotAgent` operates in one of three modes. In `'record'` mode it performs
17
+ real requests and writes the responses to a snapshot file. In `'playback'` mode
18
+ it serves responses from the snapshot file without touching the network. In
19
+ `'update'` mode it replays existing snapshots and records any request that has no
20
+ matching snapshot.
13
21
 
14
- ```javascript
15
- new SnapshotAgent([options])
16
- ```
17
-
18
- ### Parameters
19
-
20
- - **options** `Object` (optional)
21
- - **mode** `String` - The snapshot mode: `'record'`, `'playback'`, or `'update'`. Default: `'record'`
22
- - **snapshotPath** `String` - Path to the snapshot file for loading/saving
23
- - **maxSnapshots** `Number` - Maximum number of snapshots to keep in memory. Default: `Infinity`
24
- - **autoFlush** `Boolean` - Whether to automatically save snapshots to disk. Default: `false`
25
- - **flushInterval** `Number` - Interval in milliseconds for auto-flush. Default: `30000`
26
- - **matchHeaders** `Array<String>` - Specific headers to include in request matching. Default: all headers
27
- - **ignoreHeaders** `Array<String>` - Headers to ignore during request matching
28
- - **excludeHeaders** `Array<String>` - Headers to exclude from snapshots (for security)
29
- - **matchBody** `Boolean` - Whether to include request body in matching. Default: `true`
30
- - **normalizeBody** `Function` - Optional function `(body) => string` to normalize the request body before matching (e.g. strip volatile fields like timestamps). Only used when `matchBody` is `true`.
31
- - **matchQuery** `Boolean` - Whether to include query parameters in matching. Default: `true`
32
- - **normalizeQuery** `Function` - Optional function `(query: URLSearchParams) => string` to normalize query parameters before matching (e.g. strip volatile params like cache-busters). Only used when `matchQuery` is `true`.
33
- - **caseSensitive** `Boolean` - Whether header matching is case-sensitive. Default: `false`
34
- - **shouldRecord** `Function` - Callback to determine if a request should be recorded
35
- - **shouldPlayback** `Function` - Callback to determine if a request should be played back
36
- - **excludeUrls** `Array` - URL patterns (strings or RegExp) to exclude from recording/playback
37
- - All other options from `MockAgent` are supported
38
-
39
- ### Modes
40
-
41
- #### Record Mode (`'record'`)
42
- Makes real HTTP requests and saves the responses to snapshots.
43
-
44
- ```javascript
45
- import { SnapshotAgent, setGlobalDispatcher } from 'undici'
22
+ ```mjs
23
+ import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici'
46
24
 
47
- const agent = new SnapshotAgent({
25
+ const agent = new SnapshotAgent({
48
26
  mode: 'record',
49
- snapshotPath: './test/snapshots/api-calls.json'
27
+ snapshotPath: './snapshots/api.json'
50
28
  })
51
29
  setGlobalDispatcher(agent)
52
30
 
53
- // Makes real requests and records them
54
31
  const response = await fetch('https://api.example.com/users')
55
32
  const users = await response.json()
56
33
 
57
- // Save recorded snapshots
58
- await agent.saveSnapshots()
59
- ```
60
-
61
- #### Playback Mode (`'playback'`)
62
- Replays recorded responses without making real HTTP requests.
63
-
64
- ```javascript
34
+ await agent.close()
35
+ ```
36
+
37
+ Constructing a `SnapshotAgent` emits an `ExperimentalWarning` once per process.
38
+
39
+ ## Class: `SnapshotAgent`
40
+
41
+ <!-- YAML
42
+ added: v7.13.0
43
+ -->
44
+
45
+ * Extends: {MockAgent}
46
+
47
+ The captured interactions are managed by an internal `SnapshotRecorder`, which
48
+ is accessible through [`agent.getRecorder()`][]. Because `SnapshotAgent` extends
49
+ [`MockAgent`][], all `MockAgent` and [`Dispatcher`][] methods and events are also
50
+ available.
51
+
52
+ ### `new SnapshotAgent([options])`
53
+
54
+ <!-- YAML
55
+ added: v7.13.0
56
+ -->
57
+
58
+ * `options` {Object} (optional) Accepts all [`MockAgent`][] options plus the
59
+ following snapshot-specific options.
60
+ * `mode` {string} The snapshot mode. One of `'record'`, `'playback'`, or
61
+ `'update'`. **Default:** `'record'`.
62
+ * `snapshotPath` {string} Path to the snapshot file used for loading and
63
+ saving. Required when `mode` is `'playback'` or `'update'`. **Default:**
64
+ `null`.
65
+ * `maxSnapshots` {number} Maximum number of snapshots to keep in memory.
66
+ **Default:** `Infinity`.
67
+ * `autoFlush` {boolean} When `true`, snapshots are written to `snapshotPath`
68
+ automatically as they are recorded. **Default:** `false`.
69
+ * `flushInterval` {number} Interval, in milliseconds, between automatic flushes
70
+ when `autoFlush` is enabled. **Default:** `30000`.
71
+ * `matchHeaders` {string[]} Names of the only headers to include when matching
72
+ a request against a snapshot. When omitted, all headers are matched.
73
+ * `ignoreHeaders` {string[]} Header names to ignore when matching a request.
74
+ * `excludeHeaders` {string[]} Header names to strip from snapshots entirely,
75
+ so that sensitive values are never written to disk.
76
+ * `matchBody` {boolean} When `true`, the request body is included in request
77
+ matching. **Default:** `true`.
78
+ * `normalizeBody` {Function} Normalizes the request body before it is matched
79
+ or stored, for example to strip volatile fields such as timestamps. Only
80
+ applied when `matchBody` is `true`.
81
+ * `body` {string|Buffer|null|undefined} The raw request body.
82
+ * Returns: {string} The normalized body used for matching.
83
+ * `matchQuery` {boolean} When `true`, query parameters are included in request
84
+ matching. **Default:** `true`.
85
+ * `normalizeQuery` {Function} Normalizes the query string before it is matched,
86
+ for example to strip cache-busting parameters. Only applied when `matchQuery`
87
+ is `true`.
88
+ * `query` {URLSearchParams} The parsed query parameters.
89
+ * Returns: {string} The normalized query string used for matching.
90
+ * `caseSensitive` {boolean} When `true`, header matching is case-sensitive.
91
+ **Default:** `false`.
92
+ * `shouldRecord` {Function} Decides whether a given request is recorded. Only
93
+ used in `'record'` and `'update'` modes.
94
+ * `requestOpts` {Object} The dispatch options of the request.
95
+ * Returns: {boolean} `true` to record the request.
96
+ * `shouldPlayback` {Function} Decides whether a given request is served from a
97
+ snapshot. Only used in `'playback'` and `'update'` modes.
98
+ * `requestOpts` {Object} The dispatch options of the request.
99
+ * Returns: {boolean} `true` to play the request back from a snapshot.
100
+ * `excludeUrls` {Array<string|RegExp>} URL patterns that are excluded from
101
+ recording and playback. String patterns match case-insensitively as
102
+ substrings of the URL; `RegExp` patterns are tested against the URL. Matching
103
+ requests are passed through to a real [`Agent`][] instead.
104
+
105
+ Creates a new `SnapshotAgent`. Throws an [`InvalidArgumentError`][] when `mode`
106
+ is `'playback'` or `'update'` and `snapshotPath` is not provided, and throws an
107
+ `InvalidArgumentError` when `mode` is not one of the supported values.
108
+
109
+ When `mode` is `'playback'` or `'update'` and `snapshotPath` is set, snapshots
110
+ are loaded from the file automatically; a missing file is ignored until the first
111
+ request requires it.
112
+
113
+ ```mjs
65
114
  import { SnapshotAgent, setGlobalDispatcher } from 'undici'
66
115
 
67
116
  const agent = new SnapshotAgent({
68
117
  mode: 'playback',
69
- snapshotPath: './test/snapshots/api-calls.json'
70
- })
71
- setGlobalDispatcher(agent)
72
-
73
- // Uses recorded response instead of real request
74
- const response = await fetch('https://api.example.com/users')
75
- ```
76
-
77
- #### Update Mode (`'update'`)
78
- Uses existing snapshots when available, but records new ones for missing requests.
79
-
80
- ```javascript
81
- import { SnapshotAgent, setGlobalDispatcher } from 'undici'
82
-
83
- const agent = new SnapshotAgent({
84
- mode: 'update',
85
- snapshotPath: './test/snapshots/api-calls.json'
118
+ snapshotPath: './snapshots/api.json'
86
119
  })
87
120
  setGlobalDispatcher(agent)
88
-
89
- // Uses snapshot if exists, otherwise makes real request and records it
90
- const response = await fetch('https://api.example.com/new-endpoint')
91
121
  ```
92
122
 
93
- ## Instance Methods
94
-
95
- ### `agent.saveSnapshots([filePath])`
96
-
97
- Saves all recorded snapshots to a file.
98
-
99
- #### Parameters
100
-
101
- - **filePath** `String` (optional) - Path to save snapshots. Uses constructor `snapshotPath` if not provided.
102
-
103
- #### Returns
104
-
105
- `Promise<void>`
106
-
107
- ```javascript
108
- await agent.saveSnapshots('./custom-snapshots.json')
109
- ```
110
-
111
- ## Advanced Configuration
112
-
113
- ### Body Matching
114
-
115
- By default (`matchBody: true`) the full request body string is included in the snapshot key. Set it to `false` to ignore the body entirely, or use `normalizeBody` to strip volatile fields (like timestamps) before matching:
116
-
117
- ```javascript
118
- const agent = new SnapshotAgent({
119
- mode: 'playback',
120
- snapshotPath: './snapshots.json',
121
-
122
- // Match on everything except the timestamp field
123
- normalizeBody: (body) => {
124
- if (!body) return ''
125
- const parsed = JSON.parse(String(body))
126
- delete parsed.timestamp
127
- return JSON.stringify(parsed)
128
- }
129
- })
130
- ```
131
-
132
- `normalizeBody` receives the raw body (`string | Buffer | null | undefined`) and must return a `string`. It runs at both record and playback time so the hash is consistent. Two requests match the same snapshot whenever their normalized strings are identical.
133
-
134
- ### Header Filtering
135
-
136
- Control which headers are used for request matching and what gets stored in snapshots:
137
-
138
- ```javascript
139
- const agent = new SnapshotAgent({
140
- mode: 'record',
141
- snapshotPath: './snapshots.json',
142
-
143
- // Only match these specific headers
144
- matchHeaders: ['content-type', 'accept'],
145
-
146
- // Ignore these headers during matching (but still store them)
147
- ignoreHeaders: ['user-agent', 'date'],
148
-
149
- // Exclude sensitive headers from snapshots entirely
150
- excludeHeaders: ['authorization', 'x-api-key', 'cookie']
151
- })
152
- ```
123
+ The mode controls how requests are handled:
153
124
 
154
- ### Custom Request/Response Filtering
125
+ * `'record'` performs the real request and stores the response.
155
126
 
156
- Use callback functions to determine what gets recorded or played back:
127
+ ```mjs
128
+ import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici'
157
129
 
158
- ```javascript
159
- const agent = new SnapshotAgent({
160
- mode: 'record',
161
- snapshotPath: './snapshots.json',
162
-
163
- // Only record GET requests to specific endpoints
164
- shouldRecord: (requestOpts) => {
165
- const url = new URL(requestOpts.path, requestOpts.origin)
166
- return requestOpts.method === 'GET' && url.pathname.startsWith('/api/v1/')
167
- },
168
-
169
- // Skip authentication endpoints during playback
170
- shouldPlayback: (requestOpts) => {
171
- const url = new URL(requestOpts.path, requestOpts.origin)
172
- return !url.pathname.includes('/auth/')
173
- }
174
- })
175
- ```
176
-
177
- ### URL Pattern Exclusion
178
-
179
- Exclude specific URLs from recording/playback using patterns:
180
-
181
- ```javascript
182
- const agent = new SnapshotAgent({
183
- mode: 'record',
184
- snapshotPath: './snapshots.json',
185
-
186
- excludeUrls: [
187
- 'https://analytics.example.com', // String match
188
- /\/api\/v\d+\/health/, // Regex pattern
189
- 'telemetry' // Substring match
190
- ]
191
- })
192
- ```
193
-
194
- ### Memory Management
195
-
196
- Configure automatic memory and disk management:
197
-
198
- ```javascript
199
- const agent = new SnapshotAgent({
200
- mode: 'record',
201
- snapshotPath: './snapshots.json',
202
-
203
- // Keep only 1000 snapshots in memory
204
- maxSnapshots: 1000,
205
-
206
- // Automatically save to disk every 30 seconds
207
- autoFlush: true,
208
- flushInterval: 30000
209
- })
210
- ```
211
-
212
- ### Sequential Response Handling
213
-
214
- Handle multiple responses for the same request (similar to nock):
215
-
216
- ```javascript
217
- // In record mode, multiple identical requests get recorded as separate responses
218
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './sequential.json' })
130
+ const agent = new SnapshotAgent({
131
+ mode: 'record',
132
+ snapshotPath: './snapshots/api.json'
133
+ })
134
+ setGlobalDispatcher(agent)
219
135
 
220
- // First call returns response A
221
- await fetch('https://api.example.com/random')
136
+ await fetch('https://api.example.com/users')
137
+ await agent.saveSnapshots()
138
+ ```
222
139
 
223
- // Second call returns response B
224
- await fetch('https://api.example.com/random')
140
+ * `'playback'` serves the recorded response without any network access. A
141
+ request with no matching snapshot rejects with an [`UndiciError`][] whose
142
+ message begins with `No snapshot found`.
225
143
 
226
- await agent.saveSnapshots()
144
+ ```mjs
145
+ import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici'
227
146
 
228
- // In playback mode, calls return responses in sequence
229
- const playbackAgent = new SnapshotAgent({ mode: 'playback', snapshotPath: './sequential.json' })
147
+ const agent = new SnapshotAgent({
148
+ mode: 'playback',
149
+ snapshotPath: './snapshots/api.json'
150
+ })
151
+ setGlobalDispatcher(agent)
230
152
 
231
- // Returns response A
232
- const first = await fetch('https://api.example.com/random')
153
+ const response = await fetch('https://api.example.com/users')
154
+ ```
233
155
 
234
- // Returns response B
235
- const second = await fetch('https://api.example.com/random')
156
+ * `'update'` replays an existing snapshot when one is found and otherwise records
157
+ the request like `'record'` mode.
236
158
 
237
- // Third call repeats the last response (B)
238
- const third = await fetch('https://api.example.com/random')
239
- ```
159
+ ```mjs
160
+ import { SnapshotAgent, setGlobalDispatcher, fetch } from 'undici'
240
161
 
241
- ## Managing Snapshots
162
+ const agent = new SnapshotAgent({
163
+ mode: 'update',
164
+ snapshotPath: './snapshots/api.json'
165
+ })
166
+ setGlobalDispatcher(agent)
242
167
 
243
- ### Replacing Existing Snapshots
168
+ await fetch('https://api.example.com/new-endpoint')
169
+ ```
244
170
 
245
- ```javascript
246
- // Load existing snapshots
247
- await agent.loadSnapshots('./old-snapshots.json')
171
+ ### `agent.loadSnapshots([filePath])`
248
172
 
249
- // Get snapshot data
250
- const recorder = agent.getRecorder()
251
- const snapshots = recorder.getSnapshots()
173
+ <!-- YAML
174
+ added: v7.13.0
175
+ -->
252
176
 
253
- // Modify or filter snapshots
254
- const filteredSnapshots = snapshots.filter(s =>
255
- !s.request.url.includes('deprecated')
256
- )
177
+ * `filePath` {string} (optional) Path to read snapshots from. **Default:** the
178
+ `snapshotPath` given to the constructor.
179
+ * Returns: {Promise<void>} Resolves when the snapshots have been loaded.
257
180
 
258
- // Replace all snapshots
259
- agent.replaceSnapshots(filteredSnapshots.map((snapshot, index) => ({
260
- hash: `new-hash-${index}`,
261
- snapshot
262
- })))
181
+ Loads snapshots from disk into memory. In `'playback'` mode this also installs
182
+ the corresponding [`MockAgent`][] interceptors so that subsequent requests are
183
+ matched against the loaded snapshots.
263
184
 
264
- // Save updated snapshots
265
- await agent.saveSnapshots('./updated-snapshots.json')
185
+ ```mjs
186
+ await agent.loadSnapshots('./snapshots/api.json')
266
187
  ```
267
188
 
268
- ### `agent.loadSnapshots([filePath])`
269
-
270
- Loads snapshots from a file.
271
-
272
- #### Parameters
189
+ ### `agent.saveSnapshots([filePath])`
273
190
 
274
- - **filePath** `String` (optional) - Path to load snapshots from. Uses constructor `snapshotPath` if not provided.
191
+ <!-- YAML
192
+ added: v7.13.0
193
+ -->
275
194
 
276
- #### Returns
195
+ * `filePath` {string} (optional) Path to write snapshots to. **Default:** the
196
+ `snapshotPath` given to the constructor.
197
+ * Returns: {Promise<void>} Resolves when the snapshots have been written.
277
198
 
278
- `Promise<void>`
199
+ Writes all recorded snapshots to disk.
279
200
 
280
- ```javascript
281
- await agent.loadSnapshots('./existing-snapshots.json')
201
+ ```mjs
202
+ await agent.saveSnapshots('./snapshots/api.json')
282
203
  ```
283
204
 
284
205
  ### `agent.getRecorder()`
285
206
 
286
- Gets the underlying `SnapshotRecorder` instance.
207
+ <!-- YAML
208
+ added: v7.13.0
209
+ -->
287
210
 
288
- #### Returns
211
+ * Returns: {SnapshotRecorder} The internal recorder that stores the captured
212
+ interactions.
289
213
 
290
- `SnapshotRecorder`
214
+ Returns the underlying `SnapshotRecorder`. The recorder exposes lower-level
215
+ operations over the captured data, including `record()`, `findSnapshot()`,
216
+ `getSnapshots()`, `size()`, and `clear()`. The recorder is an internal type
217
+ returned for inspection; it is not exported from the package root.
291
218
 
292
- ```javascript
219
+ ```mjs
293
220
  const recorder = agent.getRecorder()
294
221
  console.log(`Recorded ${recorder.size()} interactions`)
295
222
  ```
296
223
 
297
224
  ### `agent.getMode()`
298
225
 
299
- Gets the current snapshot mode.
226
+ <!-- YAML
227
+ added: v7.13.0
228
+ -->
300
229
 
301
- #### Returns
230
+ * Returns: {string} The current mode: `'record'`, `'playback'`, or `'update'`.
302
231
 
303
- `String` - The current mode (`'record'`, `'playback'`, or `'update'`)
232
+ Returns the mode the agent was constructed with.
304
233
 
305
234
  ### `agent.clearSnapshots()`
306
235
 
307
- Clears all recorded snapshots from memory.
236
+ <!-- YAML
237
+ added: v7.13.0
238
+ -->
308
239
 
309
- ```javascript
310
- agent.clearSnapshots()
311
- ```
312
-
313
- ## Working with Different Request Types
314
-
315
- ### GET Requests
316
-
317
- ```javascript
318
- // Record mode
319
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './get-snapshots.json' })
320
- setGlobalDispatcher(agent)
321
-
322
- const response = await fetch('https://jsonplaceholder.typicode.com/posts/1')
323
- const post = await response.json()
324
-
325
- await agent.saveSnapshots()
326
- ```
327
-
328
- ### POST Requests with Body
329
-
330
- ```javascript
331
- // Record mode
332
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './post-snapshots.json' })
333
- setGlobalDispatcher(agent)
334
-
335
- const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
336
- method: 'POST',
337
- headers: { 'Content-Type': 'application/json' },
338
- body: JSON.stringify({ title: 'Test Post', body: 'Content' })
339
- })
340
-
341
- await agent.saveSnapshots()
342
- ```
343
-
344
- ### Using with `undici.request`
345
-
346
- SnapshotAgent works with all undici APIs, not just fetch:
347
-
348
- ```javascript
349
- import { SnapshotAgent, request, setGlobalDispatcher } from 'undici'
350
-
351
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './request-snapshots.json' })
352
- setGlobalDispatcher(agent)
240
+ * Returns: {undefined}
353
241
 
354
- const { statusCode, headers, body } = await request('https://api.example.com/data')
355
- const data = await body.json()
242
+ Removes all snapshots from memory.
356
243
 
357
- await agent.saveSnapshots()
358
- ```
359
-
360
- ## Test Integration
361
-
362
- ### Basic Test Setup
363
-
364
- ```javascript
365
- import { test } from 'node:test'
366
- import { SnapshotAgent, setGlobalDispatcher, getGlobalDispatcher } from 'undici'
367
-
368
- test('API integration test', async (t) => {
369
- const originalDispatcher = getGlobalDispatcher()
370
-
371
- const agent = new SnapshotAgent({
372
- mode: 'playback',
373
- snapshotPath: './test/snapshots/api-test.json'
374
- })
375
- setGlobalDispatcher(agent)
376
-
377
- t.after(() => setGlobalDispatcher(originalDispatcher))
378
-
379
- // This will use recorded data
380
- const response = await fetch('https://api.example.com/users')
381
- const users = await response.json()
382
-
383
- assert(Array.isArray(users))
384
- assert(users.length > 0)
385
- })
244
+ ```mjs
245
+ agent.clearSnapshots()
386
246
  ```
387
247
 
388
- ### Environment-Based Mode Selection
389
-
390
- ```javascript
391
- const mode = process.env.SNAPSHOT_MODE || 'playback'
392
-
393
- const agent = new SnapshotAgent({
394
- mode,
395
- snapshotPath: './test/snapshots/integration.json'
396
- })
248
+ ### `agent.resetCallCounts()`
397
249
 
398
- // Run with: SNAPSHOT_MODE=record npm test (to record)
399
- // Run with: npm test (to playback)
400
- ```
250
+ <!-- YAML
251
+ added: v7.13.0
252
+ -->
401
253
 
402
- ### Test Helper Function
254
+ * Returns: {undefined}
403
255
 
404
- ```javascript
405
- function createSnapshotAgent(testName, mode = 'playback') {
406
- return new SnapshotAgent({
407
- mode,
408
- snapshotPath: `./test/snapshots/${testName}.json`
409
- })
410
- }
256
+ Resets the call count of every snapshot to zero. Useful between tests when the
257
+ same agent is reused, since matching a snapshot in `'playback'` mode increments
258
+ its call count.
411
259
 
412
- test('user API test', async (t) => {
413
- const agent = createSnapshotAgent('user-api')
414
- setGlobalDispatcher(agent)
415
-
416
- // Test implementation...
417
- })
260
+ ```mjs
261
+ agent.resetCallCounts()
418
262
  ```
419
263
 
420
- ## Snapshot File Format
421
-
422
- Snapshots are stored as JSON with the following structure:
423
-
424
- ```json
425
- [
426
- {
427
- "hash": "dGVzdC1oYXNo...",
428
- "snapshot": {
429
- "request": {
430
- "method": "GET",
431
- "url": "https://api.example.com/users",
432
- "headers": {
433
- "authorization": "Bearer token"
434
- },
435
- "body": undefined
436
- },
437
- "response": {
438
- "statusCode": 200,
439
- "headers": {
440
- "content-type": "application/json"
441
- },
442
- "body": "eyJkYXRhIjoidGVzdCJ9", // base64 encoded
443
- "trailers": {}
444
- },
445
- "timestamp": "2024-01-01T00:00:00.000Z"
446
- }
447
- }
448
- ]
449
- ```
264
+ ### `agent.deleteSnapshot(requestOpts)`
450
265
 
451
- ## Security Considerations
266
+ <!-- YAML
267
+ added: v7.13.0
268
+ -->
452
269
 
453
- ### Sensitive Data in Snapshots
270
+ * `requestOpts` {Object} Dispatch options identifying the snapshot to remove.
271
+ * Returns: {boolean} `true` if a matching snapshot was deleted, `false` if none
272
+ was found.
454
273
 
455
- By default, SnapshotAgent records all headers and request/response data. For production use, always exclude sensitive information:
274
+ Deletes a single snapshot that matches the given request options.
456
275
 
457
- ```javascript
458
- const agent = new SnapshotAgent({
459
- mode: 'record',
460
- snapshotPath: './snapshots.json',
461
-
462
- // Exclude sensitive headers from snapshots
463
- excludeHeaders: [
464
- 'authorization',
465
- 'x-api-key',
466
- 'cookie',
467
- 'set-cookie',
468
- 'x-auth-token',
469
- 'x-csrf-token'
470
- ],
471
-
472
- // Filter out requests with sensitive data
473
- shouldRecord: (requestOpts) => {
474
- const url = new URL(requestOpts.path, requestOpts.origin)
475
-
476
- // Don't record authentication endpoints
477
- if (url.pathname.includes('/auth/') || url.pathname.includes('/login')) {
478
- return false
479
- }
480
-
481
- // Don't record if request contains sensitive body data
482
- if (requestOpts.body && typeof requestOpts.body === 'string') {
483
- const body = requestOpts.body.toLowerCase()
484
- if (body.includes('password') || body.includes('secret')) {
485
- return false
486
- }
487
- }
488
-
489
- return true
490
- }
276
+ ```mjs
277
+ const deleted = agent.deleteSnapshot({
278
+ method: 'GET',
279
+ origin: 'https://api.example.com',
280
+ path: '/users'
491
281
  })
492
282
  ```
493
283
 
494
- ### Snapshot File Security
495
-
496
- **Important**: Snapshot files may contain sensitive data. Handle them securely:
497
-
498
- - ✅ Add snapshot files to `.gitignore` if they contain real API data
499
- - ✅ Use environment-specific snapshots (dev/staging/prod)
500
- - Regularly review snapshot contents for sensitive information
501
- - Use the `excludeHeaders` option for production snapshots
502
- - Never commit snapshots with real authentication tokens
503
- - Don't share snapshot files containing personal data
504
-
505
- ```gitignore
506
- # Exclude snapshots with real data
507
- /test/snapshots/production-*.json
508
- /test/snapshots/*-real-data.json
509
-
510
- # Include sanitized test snapshots
511
- !/test/snapshots/mock-*.json
512
- ```
513
-
514
- ## Error Handling
515
-
516
- ### Missing Snapshots in Playback Mode
517
-
518
- ```javascript
519
- try {
520
- const response = await fetch('https://api.example.com/nonexistent')
521
- } catch (error) {
522
- if (error.message.includes('No snapshot found')) {
523
- // Handle missing snapshot
524
- console.log('Snapshot not found for this request')
525
- }
526
- }
527
- ```
528
-
529
- ### Handling Network Errors in Record Mode
530
-
531
- ```javascript
532
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './snapshots.json' })
533
-
534
- try {
535
- const response = await fetch('https://nonexistent-api.example.com/data')
536
- } catch (error) {
537
- // Network errors are not recorded as snapshots
538
- console.log('Network error:', error.message)
539
- }
540
- ```
541
-
542
- ## Best Practices
543
-
544
- ### 1. Organize Snapshots by Test Suite
545
-
546
- ```javascript
547
- // Use descriptive snapshot file names
548
- const agent = new SnapshotAgent({
549
- mode: 'playback',
550
- snapshotPath: `./test/snapshots/${testSuiteName}-${testName}.json`
284
+ ### `agent.getSnapshotInfo(requestOpts)`
285
+
286
+ <!-- YAML
287
+ added: v7.13.0
288
+ -->
289
+
290
+ * `requestOpts` {Object} Dispatch options identifying the snapshot to inspect.
291
+ * Returns: {Object|null} Information about the matching snapshot, or `null` when
292
+ none is found.
293
+ * `hash` {string} The hash used to key the snapshot.
294
+ * `request` {Object} The recorded request.
295
+ * `method` {string} The request method.
296
+ * `url` {string} The request URL.
297
+ * `headers` {Record<string, string>} The recorded request headers.
298
+ * `body` {string} The recorded request body, when present.
299
+ * `responseCount` {number} The number of recorded responses for the request.
300
+ * `callCount` {number} The number of times the snapshot has been matched.
301
+ * `timestamp` {string} The ISO timestamp at which the snapshot was recorded.
302
+
303
+ Returns metadata about a snapshot without returning the response payload.
304
+
305
+ ```mjs
306
+ const info = agent.getSnapshotInfo({
307
+ method: 'GET',
308
+ origin: 'https://api.example.com',
309
+ path: '/users'
551
310
  })
552
311
  ```
553
312
 
554
- ### 2. Version Control Snapshots
555
-
556
- Add snapshot files to version control to ensure consistent test behavior across environments:
313
+ ### `agent.replaceSnapshots(snapshotData)`
557
314
 
558
- ```gitignore
559
- # Include snapshots in version control
560
- !/test/snapshots/*.json
561
- ```
315
+ <!-- YAML
316
+ added: v7.13.0
317
+ -->
562
318
 
563
- ### 3. Clean Up Test Data
319
+ * `snapshotData` {Array} The snapshot data to load, replacing any existing
320
+ snapshots.
321
+ * `hash` {string} The hash used to key the snapshot.
322
+ * `snapshot` {Object} The snapshot entry.
323
+ * Returns: {undefined}
564
324
 
565
- ```javascript
566
- test('API test', async (t) => {
567
- const agent = new SnapshotAgent({
568
- mode: 'playback',
569
- snapshotPath: './test/snapshots/temp-test.json'
570
- })
571
-
572
- // Clean up after test
573
- t.after(() => {
574
- agent.clearSnapshots()
575
- })
576
- })
577
- ```
325
+ Replaces all in-memory snapshots with the provided data.
578
326
 
579
- ### 4. Snapshot Validation
327
+ ```mjs
328
+ const recorder = agent.getRecorder()
329
+ const snapshots = recorder.getSnapshots()
580
330
 
581
- ```javascript
582
- test('validate snapshot contents', async (t) => {
583
- const agent = new SnapshotAgent({
584
- mode: 'playback',
585
- snapshotPath: './test/snapshots/validation.json'
586
- })
587
-
588
- const recorder = agent.getRecorder()
589
- const snapshots = recorder.getSnapshots()
590
-
591
- // Validate snapshot structure
592
- assert(snapshots.length > 0, 'Should have recorded snapshots')
593
- assert(snapshots[0].request.url.startsWith('https://'), 'Should use HTTPS')
594
- })
331
+ agent.replaceSnapshots(snapshots.map((snapshot, index) => ({
332
+ hash: `snapshot-${index}`,
333
+ snapshot
334
+ })))
595
335
  ```
596
336
 
597
- ## Comparison with Other Tools
598
-
599
- ### vs Manual MockAgent Setup
337
+ ### `agent.close()`
600
338
 
601
- **Manual MockAgent:**
602
- ```javascript
603
- const mockAgent = new MockAgent()
604
- const mockPool = mockAgent.get('https://api.example.com')
339
+ <!-- YAML
340
+ added: v7.13.0
341
+ -->
605
342
 
606
- mockPool.intercept({
607
- path: '/users',
608
- method: 'GET'
609
- }).reply(200, [
610
- { id: 1, name: 'User 1' },
611
- { id: 2, name: 'User 2' }
612
- ])
613
- ```
343
+ * Returns: {Promise<void>} Resolves once the agent and its resources are closed.
614
344
 
615
- **SnapshotAgent:**
616
- ```javascript
617
- // Record once
618
- const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './snapshots.json' })
619
- // Real API call gets recorded automatically
345
+ Closes the agent and releases its resources. In `'record'` and `'update'` modes
346
+ the recorder is flushed to disk first. In `'playback'` mode nothing is written,
347
+ because matching a snapshot mutates its call count and saving would needlessly
348
+ rewrite the snapshot file. The real [`Agent`][], when one was created, and the
349
+ underlying [`MockAgent`][] are closed as well.
620
350
 
621
- // Use in tests
622
- const agent = new SnapshotAgent({ mode: 'playback', snapshotPath: './snapshots.json' })
623
- // Automatically replays recorded response
351
+ ```mjs
352
+ await agent.close()
624
353
  ```
625
354
 
626
- ### vs nock
627
-
628
- SnapshotAgent provides similar functionality to nock but is specifically designed for undici:
629
-
630
- - ✅ Works with all undici APIs (`request`, `stream`, `pipeline`, etc.)
631
- - ✅ Supports undici-specific features (RetryAgent, connection pooling)
632
- - ✅ Better TypeScript integration
633
- - ✅ More efficient for high-performance scenarios
634
-
635
- ## See Also
636
-
637
- - [MockAgent](/docs/docs/api/MockAgent.md) - Manual mocking for more control
638
- - [MockCallHistory](/docs/docs/api/MockCallHistory.md) - Inspecting request history
639
- - [Testing Best Practices](/docs/docs/best-practices/writing-tests.md) - General testing guidance
355
+ [`Agent`]: Agent.md#class-agent
356
+ [`Dispatcher`]: Dispatcher.md#class-dispatcher
357
+ [`InvalidArgumentError`]: Errors.md#undicierrorsundiciinvalidargumenterror
358
+ [`MockAgent`]: MockAgent.md#class-mockagent
359
+ [`UndiciError`]: Errors.md#undicierrorsundicierror
360
+ [`agent.getRecorder()`]: #agentgetrecorder
361
+ [`setGlobalDispatcher()`]: Dispatcher.md#setglobaldispatcherdispatcher