@@ -5,14 +5,15 @@ const MockAgent = require('./mock-agent')
55const { SnapshotRecorder } = require ( './snapshot-recorder' )
66const WrapHandler = require ( '../handler/wrap-handler' )
77const { InvalidArgumentError, UndiciError } = require ( '../core/errors' )
8+ const { validateSnapshotMode } = require ( './snapshot-utils' )
89
910const kSnapshotRecorder = Symbol ( 'kSnapshotRecorder' )
1011const kSnapshotMode = Symbol ( 'kSnapshotMode' )
1112const kSnapshotPath = Symbol ( 'kSnapshotPath' )
1213const kSnapshotLoaded = Symbol ( 'kSnapshotLoaded' )
1314const kRealAgent = Symbol ( 'kRealAgent' )
1415
15- // Static flag to ensure warning is only emitted once
16+ // Static flag to ensure warning is only emitted once per process
1617let warningEmitted = false
1718
1819class SnapshotAgent extends MockAgent {
@@ -26,26 +27,24 @@ class SnapshotAgent extends MockAgent {
2627 warningEmitted = true
2728 }
2829
29- const mockOptions = { ...opts }
30- delete mockOptions . mode
31- delete mockOptions . snapshotPath
30+ const {
31+ mode = 'record' ,
32+ snapshotPath = null ,
33+ ...mockAgentOpts
34+ } = opts
3235
33- super ( mockOptions )
36+ super ( mockAgentOpts )
3437
35- // Validate mode option
36- const validModes = [ 'record' , 'playback' , 'update' ]
37- const mode = opts . mode || 'record'
38- if ( ! validModes . includes ( mode ) ) {
39- throw new InvalidArgumentError ( `Invalid snapshot mode: ${ mode } . Must be one of: ${ validModes . join ( ', ' ) } ` )
40- }
38+ validateSnapshotMode ( mode )
4139
4240 // Validate snapshotPath is provided when required
43- if ( ( mode === 'playback' || mode === 'update' ) && ! opts . snapshotPath ) {
41+ if ( ( mode === 'playback' || mode === 'update' ) && ! snapshotPath ) {
4442 throw new InvalidArgumentError ( `snapshotPath is required when mode is '${ mode } '` )
4543 }
4644
4745 this [ kSnapshotMode ] = mode
48- this [ kSnapshotPath ] = opts . snapshotPath
46+ this [ kSnapshotPath ] = snapshotPath
47+
4948 this [ kSnapshotRecorder ] = new SnapshotRecorder ( {
5049 snapshotPath : this [ kSnapshotPath ] ,
5150 mode : this [ kSnapshotMode ] ,
@@ -85,18 +84,18 @@ class SnapshotAgent extends MockAgent {
8584 // Ensure snapshots are loaded
8685 if ( ! this [ kSnapshotLoaded ] ) {
8786 // Need to load asynchronously, delegate to async version
88- return this . _asyncDispatch ( opts , handler )
87+ return this . #asyncDispatch ( opts , handler )
8988 }
9089
9190 // Try to find existing snapshot (synchronous)
9291 const snapshot = this [ kSnapshotRecorder ] . findSnapshot ( opts )
9392
9493 if ( snapshot ) {
9594 // Use recorded response (synchronous)
96- return this . _replaySnapshot ( snapshot , handler )
95+ return this . #replaySnapshot ( snapshot , handler )
9796 } else if ( mode === 'update' ) {
9897 // Make real request and record it (async required)
99- return this . _recordAndReplay ( opts , handler )
98+ return this . #recordAndReplay ( opts , handler )
10099 } else {
101100 // Playback mode but no snapshot found
102101 const error = new UndiciError ( `No snapshot found for ${ opts . method || 'GET' } ${ opts . path } ` )
@@ -108,24 +107,22 @@ class SnapshotAgent extends MockAgent {
108107 }
109108 } else if ( mode === 'record' ) {
110109 // Record mode - make real request and save response (async required)
111- return this . _recordAndReplay ( opts , handler )
112- } else {
113- throw new InvalidArgumentError ( `Invalid snapshot mode: ${ mode } . Must be 'record', 'playback', or 'update'` )
110+ return this . #recordAndReplay( opts , handler )
114111 }
115112 }
116113
117114 /**
118115 * Async version of dispatch for when we need to load snapshots first
119116 */
120- async _asyncDispatch ( opts , handler ) {
117+ async #asyncDispatch ( opts , handler ) {
121118 await this . loadSnapshots ( )
122119 return this . dispatch ( opts , handler )
123120 }
124121
125122 /**
126123 * Records a real request and replays the response
127124 */
128- _recordAndReplay ( opts , handler ) {
125+ #recordAndReplay ( opts , handler ) {
129126 const responseData = {
130127 statusCode : null ,
131128 headers : { } ,
@@ -180,8 +177,12 @@ class SnapshotAgent extends MockAgent {
180177
181178 /**
182179 * Replays a recorded response
180+ *
181+ * @param {Object } snapshot - The recorded snapshot to replay.
182+ * @param {Object } handler - The handler to call with the response data.
183+ * @returns {void }
183184 */
184- _replaySnapshot ( snapshot , handler ) {
185+ #replaySnapshot ( snapshot , handler ) {
185186 try {
186187 const { response } = snapshot
187188
@@ -213,19 +214,25 @@ class SnapshotAgent extends MockAgent {
213214
214215 /**
215216 * Loads snapshots from file
217+ *
218+ * @param {string } [filePath] - Optional file path to load snapshots from.
219+ * @returns {Promise<void> } - Resolves when snapshots are loaded.
216220 */
217221 async loadSnapshots ( filePath ) {
218222 await this [ kSnapshotRecorder ] . loadSnapshots ( filePath || this [ kSnapshotPath ] )
219223 this [ kSnapshotLoaded ] = true
220224
221225 // In playback mode, set up MockAgent interceptors for all snapshots
222226 if ( this [ kSnapshotMode ] === 'playback' ) {
223- this . _setupMockInterceptors ( )
227+ this . #setupMockInterceptors ( )
224228 }
225229 }
226230
227231 /**
228232 * Saves snapshots to file
233+ *
234+ * @param {string } [filePath] - Optional file path to save snapshots to.
235+ * @returns {Promise<void> } - Resolves when snapshots are saved.
229236 */
230237 async saveSnapshots ( filePath ) {
231238 return this [ kSnapshotRecorder ] . saveSnapshots ( filePath || this [ kSnapshotPath ] )
@@ -242,9 +249,9 @@ class SnapshotAgent extends MockAgent {
242249 *
243250 * Called automatically when loading snapshots in playback mode.
244251 *
245- * @private
252+ * @returns { void }
246253 */
247- _setupMockInterceptors ( ) {
254+ #setupMockInterceptors ( ) {
248255 for ( const snapshot of this [ kSnapshotRecorder ] . getSnapshots ( ) ) {
249256 const { request, responses, response } = snapshot
250257 const url = new URL ( request . url )
@@ -269,55 +276,68 @@ class SnapshotAgent extends MockAgent {
269276
270277 /**
271278 * Gets the snapshot recorder
279+ * @return {SnapshotRecorder } - The snapshot recorder instance
272280 */
273281 getRecorder ( ) {
274282 return this [ kSnapshotRecorder ]
275283 }
276284
277285 /**
278286 * Gets the current mode
287+ * @return {import('./snapshot-utils').SnapshotMode } - The current snapshot mode
279288 */
280289 getMode ( ) {
281290 return this [ kSnapshotMode ]
282291 }
283292
284293 /**
285294 * Clears all snapshots
295+ * @returns {void }
286296 */
287297 clearSnapshots ( ) {
288298 this [ kSnapshotRecorder ] . clear ( )
289299 }
290300
291301 /**
292302 * Resets call counts for all snapshots (useful for test cleanup)
303+ * @returns {void }
293304 */
294305 resetCallCounts ( ) {
295306 this [ kSnapshotRecorder ] . resetCallCounts ( )
296307 }
297308
298309 /**
299310 * Deletes a specific snapshot by request options
311+ * @param {import('./snapshot-recorder').SnapshotRequestOptions } requestOpts - Request options to identify the snapshot
312+ * @return {Promise<boolean> } - Returns true if the snapshot was deleted, false if not found
300313 */
301314 deleteSnapshot ( requestOpts ) {
302315 return this [ kSnapshotRecorder ] . deleteSnapshot ( requestOpts )
303316 }
304317
305318 /**
306319 * Gets information about a specific snapshot
320+ * @returns {import('./snapshot-recorder').SnapshotInfo|null } - Snapshot information or null if not found
307321 */
308322 getSnapshotInfo ( requestOpts ) {
309323 return this [ kSnapshotRecorder ] . getSnapshotInfo ( requestOpts )
310324 }
311325
312326 /**
313327 * Replaces all snapshots with new data (full replacement)
328+ * @param {Array<{hash: string; snapshot: import('./snapshot-recorder').SnapshotEntryshotEntry}>|Record<string, import('./snapshot-recorder').SnapshotEntry> } snapshotData - New snapshot data to replace existing snapshots
329+ * @returns {void }
314330 */
315331 replaceSnapshots ( snapshotData ) {
316332 this [ kSnapshotRecorder ] . replaceSnapshots ( snapshotData )
317333 }
318334
335+ /**
336+ * Closes the agent, saving snapshots and cleaning up resources.
337+ *
338+ * @returns {Promise<void> }
339+ */
319340 async close ( ) {
320- // Close recorder (saves snapshots and cleans up timers)
321341 await this [ kSnapshotRecorder ] . close ( )
322342 await this [ kRealAgent ] ?. close ( )
323343 await super . close ( )
0 commit comments