{
  "type": "module",
  "source": "doc/api/api-snapshotagent.md",
  "modules": [
    {
      "textRaw": "SnapshotAgent",
      "name": "snapshotagent",
      "type": "module",
      "desc": "<p>The <code>SnapshotAgent</code> provides a powerful way to record and replay HTTP requests for testing purposes. It extends <code>MockAgent</code> to enable automatic snapshot testing, eliminating the need to manually define mock responses.</p>",
      "modules": [
        {
          "textRaw": "Use Cases",
          "name": "use_cases",
          "type": "module",
          "desc": "<ul>\n<li><strong>Integration Testing</strong>: Record real API interactions and replay them in tests</li>\n<li><strong>Offline Development</strong>: Work with APIs without network connectivity</li>\n<li><strong>Consistent Test Data</strong>: Ensure tests use the same responses across runs</li>\n<li><strong>API Contract Testing</strong>: Capture and validate API behavior over time</li>\n</ul>",
          "displayName": "Use Cases"
        },
        {
          "textRaw": "Constructor",
          "name": "constructor",
          "type": "module",
          "desc": "<pre><code class=\"language-javascript\">new SnapshotAgent([options])\n</code></pre>",
          "modules": [
            {
              "textRaw": "Parameters",
              "name": "parameters",
              "type": "module",
              "desc": "<ul>\n<li><strong>options</strong> <code>Object</code> (optional)\n<ul>\n<li><strong>mode</strong> <code>String</code> - The snapshot mode: <code>'record'</code>, <code>'playback'</code>, or <code>'update'</code>. Default: <code>'record'</code></li>\n<li><strong>snapshotPath</strong> <code>String</code> - Path to the snapshot file for loading/saving</li>\n<li><strong>maxSnapshots</strong> <code>Number</code> - Maximum number of snapshots to keep in memory. Default: <code>Infinity</code></li>\n<li><strong>autoFlush</strong> <code>Boolean</code> - Whether to automatically save snapshots to disk. Default: <code>false</code></li>\n<li><strong>flushInterval</strong> <code>Number</code> - Interval in milliseconds for auto-flush. Default: <code>30000</code></li>\n<li><strong>matchHeaders</strong> <code>Array&#x3C;String></code> - Specific headers to include in request matching. Default: all headers</li>\n<li><strong>ignoreHeaders</strong> <code>Array&#x3C;String></code> - Headers to ignore during request matching</li>\n<li><strong>excludeHeaders</strong> <code>Array&#x3C;String></code> - Headers to exclude from snapshots (for security)</li>\n<li><strong>matchBody</strong> <code>Boolean</code> - Whether to include request body in matching. Default: <code>true</code></li>\n<li><strong>normalizeBody</strong> <code>Function</code> - Optional function <code>(body) => string</code> to normalize the request body before matching (e.g. strip volatile fields like timestamps). Only used when <code>matchBody</code> is <code>true</code>.</li>\n<li><strong>matchQuery</strong> <code>Boolean</code> - Whether to include query parameters in matching. Default: <code>true</code></li>\n<li><strong>normalizeQuery</strong> <code>Function</code> - Optional function <code>(query: URLSearchParams) => string</code> to normalize query parameters before matching (e.g. strip volatile params like cache-busters). Only used when <code>matchQuery</code> is <code>true</code>.</li>\n<li><strong>caseSensitive</strong> <code>Boolean</code> - Whether header matching is case-sensitive. Default: <code>false</code></li>\n<li><strong>shouldRecord</strong> <code>Function</code> - Callback to determine if a request should be recorded</li>\n<li><strong>shouldPlayback</strong> <code>Function</code> - Callback to determine if a request should be played back</li>\n<li><strong>excludeUrls</strong> <code>Array</code> - URL patterns (strings or RegExp) to exclude from recording/playback</li>\n<li>All other options from <code>MockAgent</code> are supported</li>\n</ul>\n</li>\n</ul>",
              "displayName": "Parameters"
            },
            {
              "textRaw": "Modes",
              "name": "modes",
              "type": "module",
              "modules": [
                {
                  "textRaw": "Record Mode (`'record'`)",
                  "name": "record_mode_(`'record'`)",
                  "type": "module",
                  "desc": "<p>Makes real HTTP requests and saves the responses to snapshots.</p>\n<pre><code class=\"language-javascript\">import { SnapshotAgent, setGlobalDispatcher } from 'undici'\n\nconst agent = new SnapshotAgent({ \n  mode: 'record',\n  snapshotPath: './test/snapshots/api-calls.json'\n})\nsetGlobalDispatcher(agent)\n\n// Makes real requests and records them\nconst response = await fetch('https://api.example.com/users')\nconst users = await response.json()\n\n// Save recorded snapshots\nawait agent.saveSnapshots()\n</code></pre>",
                  "displayName": "Record Mode (`'record'`)"
                },
                {
                  "textRaw": "Playback Mode (`'playback'`)",
                  "name": "playback_mode_(`'playback'`)",
                  "type": "module",
                  "desc": "<p>Replays recorded responses without making real HTTP requests.</p>\n<pre><code class=\"language-javascript\">import { SnapshotAgent, setGlobalDispatcher } from 'undici'\n\nconst agent = new SnapshotAgent({\n  mode: 'playback',\n  snapshotPath: './test/snapshots/api-calls.json'\n})\nsetGlobalDispatcher(agent)\n\n// Uses recorded response instead of real request\nconst response = await fetch('https://api.example.com/users')\n</code></pre>",
                  "displayName": "Playback Mode (`'playback'`)"
                },
                {
                  "textRaw": "Update Mode (`'update'`)",
                  "name": "update_mode_(`'update'`)",
                  "type": "module",
                  "desc": "<p>Uses existing snapshots when available, but records new ones for missing requests.</p>\n<pre><code class=\"language-javascript\">import { SnapshotAgent, setGlobalDispatcher } from 'undici'\n\nconst agent = new SnapshotAgent({\n  mode: 'update',\n  snapshotPath: './test/snapshots/api-calls.json'\n})\nsetGlobalDispatcher(agent)\n\n// Uses snapshot if exists, otherwise makes real request and records it\nconst response = await fetch('https://api.example.com/new-endpoint')\n</code></pre>",
                  "displayName": "Update Mode (`'update'`)"
                }
              ],
              "displayName": "Modes"
            }
          ],
          "displayName": "Constructor"
        },
        {
          "textRaw": "Instance Methods",
          "name": "instance_methods",
          "type": "module",
          "methods": [
            {
              "textRaw": "`agent.saveSnapshots([filePath])`",
              "name": "saveSnapshots",
              "type": "method",
              "signatures": [
                {
                  "params": [
                    {
                      "name": "filePath",
                      "optional": true
                    }
                  ]
                }
              ],
              "desc": "<p>Saves all recorded snapshots to a file.</p>",
              "modules": [
                {
                  "textRaw": "Parameters",
                  "name": "parameters",
                  "type": "module",
                  "desc": "<ul>\n<li><strong>filePath</strong> <code>String</code> (optional) - Path to save snapshots. Uses constructor <code>snapshotPath</code> if not provided.</li>\n</ul>",
                  "displayName": "Parameters"
                },
                {
                  "textRaw": "Returns",
                  "name": "returns",
                  "type": "module",
                  "desc": "<p><code>Promise&#x3C;void></code></p>\n<pre><code class=\"language-javascript\">await agent.saveSnapshots('./custom-snapshots.json')\n</code></pre>",
                  "displayName": "Returns"
                }
              ]
            }
          ],
          "displayName": "Instance Methods"
        },
        {
          "textRaw": "Advanced Configuration",
          "name": "advanced_configuration",
          "type": "module",
          "modules": [
            {
              "textRaw": "Body Matching",
              "name": "body_matching",
              "type": "module",
              "desc": "<p>By default (<code>matchBody: true</code>) the full request body string is included in the snapshot key. Set it to <code>false</code> to ignore the body entirely, or use <code>normalizeBody</code> to strip volatile fields (like timestamps) before matching:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'playback',\n  snapshotPath: './snapshots.json',\n\n  // Match on everything except the timestamp field\n  normalizeBody: (body) => {\n    if (!body) return ''\n    const parsed = JSON.parse(String(body))\n    delete parsed.timestamp\n    return JSON.stringify(parsed)\n  }\n})\n</code></pre>\n<p><code>normalizeBody</code> receives the raw body (<code>string | Buffer | null | undefined</code>) and must return a <code>string</code>. 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.</p>",
              "displayName": "Body Matching"
            },
            {
              "textRaw": "Header Filtering",
              "name": "header_filtering",
              "type": "module",
              "desc": "<p>Control which headers are used for request matching and what gets stored in snapshots:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  \n  // Only match these specific headers\n  matchHeaders: ['content-type', 'accept'],\n  \n  // Ignore these headers during matching (but still store them)\n  ignoreHeaders: ['user-agent', 'date'],\n  \n  // Exclude sensitive headers from snapshots entirely\n  excludeHeaders: ['authorization', 'x-api-key', 'cookie']\n})\n</code></pre>",
              "displayName": "Header Filtering"
            },
            {
              "textRaw": "Custom Request/Response Filtering",
              "name": "custom_request/response_filtering",
              "type": "module",
              "desc": "<p>Use callback functions to determine what gets recorded or played back:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  \n  // Only record GET requests to specific endpoints\n  shouldRecord: (requestOpts) => {\n    const url = new URL(requestOpts.path, requestOpts.origin)\n    return requestOpts.method === 'GET' &#x26;&#x26; url.pathname.startsWith('/api/v1/')\n  },\n  \n  // Skip authentication endpoints during playback\n  shouldPlayback: (requestOpts) => {\n    const url = new URL(requestOpts.path, requestOpts.origin)\n    return !url.pathname.includes('/auth/')\n  }\n})\n</code></pre>",
              "displayName": "Custom Request/Response Filtering"
            },
            {
              "textRaw": "URL Pattern Exclusion",
              "name": "url_pattern_exclusion",
              "type": "module",
              "desc": "<p>Exclude specific URLs from recording/playback using patterns:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  \n  excludeUrls: [\n    'https://analytics.example.com',  // String match\n    /\\/api\\/v\\d+\\/health/,           // Regex pattern\n    'telemetry'                      // Substring match\n  ]\n})\n</code></pre>",
              "displayName": "URL Pattern Exclusion"
            },
            {
              "textRaw": "Memory Management",
              "name": "memory_management",
              "type": "module",
              "desc": "<p>Configure automatic memory and disk management:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  \n  // Keep only 1000 snapshots in memory\n  maxSnapshots: 1000,\n  \n  // Automatically save to disk every 30 seconds\n  autoFlush: true,\n  flushInterval: 30000\n})\n</code></pre>",
              "displayName": "Memory Management"
            },
            {
              "textRaw": "Sequential Response Handling",
              "name": "sequential_response_handling",
              "type": "module",
              "desc": "<p>Handle multiple responses for the same request (similar to nock):</p>\n<pre><code class=\"language-javascript\">// In record mode, multiple identical requests get recorded as separate responses\nconst agent = new SnapshotAgent({ mode: 'record', snapshotPath: './sequential.json' })\n\n// First call returns response A\nawait fetch('https://api.example.com/random')\n\n// Second call returns response B  \nawait fetch('https://api.example.com/random')\n\nawait agent.saveSnapshots()\n\n// In playback mode, calls return responses in sequence\nconst playbackAgent = new SnapshotAgent({ mode: 'playback', snapshotPath: './sequential.json' })\n\n// Returns response A\nconst first = await fetch('https://api.example.com/random')\n\n// Returns response B\nconst second = await fetch('https://api.example.com/random')\n\n// Third call repeats the last response (B)\nconst third = await fetch('https://api.example.com/random')\n</code></pre>",
              "displayName": "Sequential Response Handling"
            }
          ],
          "displayName": "Advanced Configuration"
        },
        {
          "textRaw": "Managing Snapshots",
          "name": "managing_snapshots",
          "type": "module",
          "modules": [
            {
              "textRaw": "Replacing Existing Snapshots",
              "name": "replacing_existing_snapshots",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">// Load existing snapshots\nawait agent.loadSnapshots('./old-snapshots.json')\n\n// Get snapshot data\nconst recorder = agent.getRecorder()\nconst snapshots = recorder.getSnapshots()\n\n// Modify or filter snapshots\nconst filteredSnapshots = snapshots.filter(s => \n  !s.request.url.includes('deprecated')\n)\n\n// Replace all snapshots\nagent.replaceSnapshots(filteredSnapshots.map((snapshot, index) => ({\n  hash: `new-hash-${index}`,\n  snapshot\n})))\n\n// Save updated snapshots\nawait agent.saveSnapshots('./updated-snapshots.json')\n</code></pre>",
              "displayName": "Replacing Existing Snapshots"
            }
          ],
          "methods": [
            {
              "textRaw": "`agent.loadSnapshots([filePath])`",
              "name": "loadSnapshots",
              "type": "method",
              "signatures": [
                {
                  "params": [
                    {
                      "name": "filePath",
                      "optional": true
                    }
                  ]
                }
              ],
              "desc": "<p>Loads snapshots from a file.</p>",
              "modules": [
                {
                  "textRaw": "Parameters",
                  "name": "parameters",
                  "type": "module",
                  "desc": "<ul>\n<li><strong>filePath</strong> <code>String</code> (optional) - Path to load snapshots from. Uses constructor <code>snapshotPath</code> if not provided.</li>\n</ul>",
                  "displayName": "Parameters"
                },
                {
                  "textRaw": "Returns",
                  "name": "returns",
                  "type": "module",
                  "desc": "<p><code>Promise&#x3C;void></code></p>\n<pre><code class=\"language-javascript\">await agent.loadSnapshots('./existing-snapshots.json')\n</code></pre>",
                  "displayName": "Returns"
                }
              ]
            },
            {
              "textRaw": "`agent.getRecorder()`",
              "name": "getRecorder",
              "type": "method",
              "signatures": [
                {
                  "params": []
                }
              ],
              "desc": "<p>Gets the underlying <code>SnapshotRecorder</code> instance.</p>",
              "modules": [
                {
                  "textRaw": "Returns",
                  "name": "returns",
                  "type": "module",
                  "desc": "<p><code>SnapshotRecorder</code></p>\n<pre><code class=\"language-javascript\">const recorder = agent.getRecorder()\nconsole.log(`Recorded ${recorder.size()} interactions`)\n</code></pre>",
                  "displayName": "Returns"
                }
              ]
            },
            {
              "textRaw": "`agent.getMode()`",
              "name": "getMode",
              "type": "method",
              "signatures": [
                {
                  "params": []
                }
              ],
              "desc": "<p>Gets the current snapshot mode.</p>",
              "modules": [
                {
                  "textRaw": "Returns",
                  "name": "returns",
                  "type": "module",
                  "desc": "<p><code>String</code> - The current mode (<code>'record'</code>, <code>'playback'</code>, or <code>'update'</code>)</p>",
                  "displayName": "Returns"
                }
              ]
            },
            {
              "textRaw": "`agent.clearSnapshots()`",
              "name": "clearSnapshots",
              "type": "method",
              "signatures": [
                {
                  "params": []
                }
              ],
              "desc": "<p>Clears all recorded snapshots from memory.</p>\n<pre><code class=\"language-javascript\">agent.clearSnapshots()\n</code></pre>"
            }
          ],
          "displayName": "Managing Snapshots"
        },
        {
          "textRaw": "Working with Different Request Types",
          "name": "working_with_different_request_types",
          "type": "module",
          "modules": [
            {
              "textRaw": "GET Requests",
              "name": "get_requests",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">// Record mode\nconst agent = new SnapshotAgent({ mode: 'record', snapshotPath: './get-snapshots.json' })\nsetGlobalDispatcher(agent)\n\nconst response = await fetch('https://jsonplaceholder.typicode.com/posts/1')\nconst post = await response.json()\n\nawait agent.saveSnapshots()\n</code></pre>",
              "displayName": "GET Requests"
            },
            {
              "textRaw": "POST Requests with Body",
              "name": "post_requests_with_body",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">// Record mode\nconst agent = new SnapshotAgent({ mode: 'record', snapshotPath: './post-snapshots.json' })\nsetGlobalDispatcher(agent)\n\nconst response = await fetch('https://jsonplaceholder.typicode.com/posts', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ title: 'Test Post', body: 'Content' })\n})\n\nawait agent.saveSnapshots()\n</code></pre>",
              "displayName": "POST Requests with Body"
            },
            {
              "textRaw": "Using with `undici.request`",
              "name": "using_with_`undici.request`",
              "type": "module",
              "desc": "<p>SnapshotAgent works with all undici APIs, not just fetch:</p>\n<pre><code class=\"language-javascript\">import { SnapshotAgent, request, setGlobalDispatcher } from 'undici'\n\nconst agent = new SnapshotAgent({ mode: 'record', snapshotPath: './request-snapshots.json' })\nsetGlobalDispatcher(agent)\n\nconst { statusCode, headers, body } = await request('https://api.example.com/data')\nconst data = await body.json()\n\nawait agent.saveSnapshots()\n</code></pre>",
              "displayName": "Using with `undici.request`"
            }
          ],
          "displayName": "Working with Different Request Types"
        },
        {
          "textRaw": "Test Integration",
          "name": "test_integration",
          "type": "module",
          "modules": [
            {
              "textRaw": "Basic Test Setup",
              "name": "basic_test_setup",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">import { test } from 'node:test'\nimport { SnapshotAgent, setGlobalDispatcher, getGlobalDispatcher } from 'undici'\n\ntest('API integration test', async (t) => {\n  const originalDispatcher = getGlobalDispatcher()\n  \n  const agent = new SnapshotAgent({\n    mode: 'playback',\n    snapshotPath: './test/snapshots/api-test.json'\n  })\n  setGlobalDispatcher(agent)\n  \n  t.after(() => setGlobalDispatcher(originalDispatcher))\n  \n  // This will use recorded data\n  const response = await fetch('https://api.example.com/users')\n  const users = await response.json()\n  \n  assert(Array.isArray(users))\n  assert(users.length > 0)\n})\n</code></pre>",
              "displayName": "Basic Test Setup"
            },
            {
              "textRaw": "Environment-Based Mode Selection",
              "name": "environment-based_mode_selection",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">const mode = process.env.SNAPSHOT_MODE || 'playback'\n\nconst agent = new SnapshotAgent({\n  mode,\n  snapshotPath: './test/snapshots/integration.json'\n})\n\n// Run with: SNAPSHOT_MODE=record npm test (to record)\n// Run with: npm test (to playback)\n</code></pre>",
              "displayName": "Environment-Based Mode Selection"
            },
            {
              "textRaw": "Test Helper Function",
              "name": "test_helper_function",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">function createSnapshotAgent(testName, mode = 'playback') {\n  return new SnapshotAgent({\n    mode,\n    snapshotPath: `./test/snapshots/${testName}.json`\n  })\n}\n\ntest('user API test', async (t) => {\n  const agent = createSnapshotAgent('user-api')\n  setGlobalDispatcher(agent)\n  \n  // Test implementation...\n})\n</code></pre>",
              "displayName": "Test Helper Function"
            }
          ],
          "displayName": "Test Integration"
        },
        {
          "textRaw": "Snapshot File Format",
          "name": "snapshot_file_format",
          "type": "module",
          "desc": "<p>Snapshots are stored as JSON with the following structure:</p>\n<pre><code class=\"language-json\">[\n  {\n    \"hash\": \"dGVzdC1oYXNo...\",\n    \"snapshot\": {\n      \"request\": {\n        \"method\": \"GET\",\n        \"url\": \"https://api.example.com/users\",\n        \"headers\": {\n          \"authorization\": \"Bearer token\"\n        },\n        \"body\": undefined\n      },\n      \"response\": {\n        \"statusCode\": 200,\n        \"headers\": {\n          \"content-type\": \"application/json\"\n        },\n        \"body\": \"eyJkYXRhIjoidGVzdCJ9\", // base64 encoded\n        \"trailers\": {}\n      },\n      \"timestamp\": \"2024-01-01T00:00:00.000Z\"\n    }\n  }\n]\n</code></pre>",
          "displayName": "Snapshot File Format"
        },
        {
          "textRaw": "Security Considerations",
          "name": "security_considerations",
          "type": "module",
          "modules": [
            {
              "textRaw": "Sensitive Data in Snapshots",
              "name": "sensitive_data_in_snapshots",
              "type": "module",
              "desc": "<p>By default, SnapshotAgent records all headers and request/response data. For production use, always exclude sensitive information:</p>\n<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({\n  mode: 'record',\n  snapshotPath: './snapshots.json',\n  \n  // Exclude sensitive headers from snapshots\n  excludeHeaders: [\n    'authorization',\n    'x-api-key', \n    'cookie',\n    'set-cookie',\n    'x-auth-token',\n    'x-csrf-token'\n  ],\n  \n  // Filter out requests with sensitive data\n  shouldRecord: (requestOpts) => {\n    const url = new URL(requestOpts.path, requestOpts.origin)\n    \n    // Don't record authentication endpoints\n    if (url.pathname.includes('/auth/') || url.pathname.includes('/login')) {\n      return false\n    }\n    \n    // Don't record if request contains sensitive body data\n    if (requestOpts.body &#x26;&#x26; typeof requestOpts.body === 'string') {\n      const body = requestOpts.body.toLowerCase()\n      if (body.includes('password') || body.includes('secret')) {\n        return false\n      }\n    }\n    \n    return true\n  }\n})\n</code></pre>",
              "displayName": "Sensitive Data in Snapshots"
            },
            {
              "textRaw": "Snapshot File Security",
              "name": "snapshot_file_security",
              "type": "module",
              "desc": "<p><strong>Important</strong>: Snapshot files may contain sensitive data. Handle them securely:</p>\n<ul>\n<li>✅ Add snapshot files to <code>.gitignore</code> if they contain real API data</li>\n<li>✅ Use environment-specific snapshots (dev/staging/prod)</li>\n<li>✅ Regularly review snapshot contents for sensitive information</li>\n<li>✅ Use the <code>excludeHeaders</code> option for production snapshots</li>\n<li>❌ Never commit snapshots with real authentication tokens</li>\n<li>❌ Don't share snapshot files containing personal data</li>\n</ul>\n<pre><code class=\"language-gitignore\"># Exclude snapshots with real data\n/test/snapshots/production-*.json\n/test/snapshots/*-real-data.json\n\n# Include sanitized test snapshots\n!/test/snapshots/mock-*.json\n</code></pre>",
              "displayName": "Snapshot File Security"
            }
          ],
          "displayName": "Security Considerations"
        },
        {
          "textRaw": "Error Handling",
          "name": "error_handling",
          "type": "module",
          "modules": [
            {
              "textRaw": "Missing Snapshots in Playback Mode",
              "name": "missing_snapshots_in_playback_mode",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">try {\n  const response = await fetch('https://api.example.com/nonexistent')\n} catch (error) {\n  if (error.message.includes('No snapshot found')) {\n    // Handle missing snapshot\n    console.log('Snapshot not found for this request')\n  }\n}\n</code></pre>",
              "displayName": "Missing Snapshots in Playback Mode"
            },
            {
              "textRaw": "Handling Network Errors in Record Mode",
              "name": "handling_network_errors_in_record_mode",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">const agent = new SnapshotAgent({ mode: 'record', snapshotPath: './snapshots.json' })\n\ntry {\n  const response = await fetch('https://nonexistent-api.example.com/data')\n} catch (error) {\n  // Network errors are not recorded as snapshots\n  console.log('Network error:', error.message)\n}\n</code></pre>",
              "displayName": "Handling Network Errors in Record Mode"
            }
          ],
          "displayName": "Error Handling"
        },
        {
          "textRaw": "Best Practices",
          "name": "best_practices",
          "type": "module",
          "modules": [
            {
              "textRaw": "1. Organize Snapshots by Test Suite",
              "name": "1._organize_snapshots_by_test_suite",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">// Use descriptive snapshot file names\nconst agent = new SnapshotAgent({\n  mode: 'playback',\n  snapshotPath: `./test/snapshots/${testSuiteName}-${testName}.json`\n})\n</code></pre>",
              "displayName": "1. Organize Snapshots by Test Suite"
            },
            {
              "textRaw": "2. Version Control Snapshots",
              "name": "2._version_control_snapshots",
              "type": "module",
              "desc": "<p>Add snapshot files to version control to ensure consistent test behavior across environments:</p>\n<pre><code class=\"language-gitignore\"># Include snapshots in version control\n!/test/snapshots/*.json\n</code></pre>",
              "displayName": "2. Version Control Snapshots"
            },
            {
              "textRaw": "3. Clean Up Test Data",
              "name": "3._clean_up_test_data",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">test('API test', async (t) => {\n  const agent = new SnapshotAgent({\n    mode: 'playback',\n    snapshotPath: './test/snapshots/temp-test.json'\n  })\n  \n  // Clean up after test\n  t.after(() => {\n    agent.clearSnapshots()\n  })\n})\n</code></pre>",
              "displayName": "3. Clean Up Test Data"
            },
            {
              "textRaw": "4. Snapshot Validation",
              "name": "4._snapshot_validation",
              "type": "module",
              "desc": "<pre><code class=\"language-javascript\">test('validate snapshot contents', async (t) => {\n  const agent = new SnapshotAgent({\n    mode: 'playback',\n    snapshotPath: './test/snapshots/validation.json'\n  })\n  \n  const recorder = agent.getRecorder()\n  const snapshots = recorder.getSnapshots()\n  \n  // Validate snapshot structure\n  assert(snapshots.length > 0, 'Should have recorded snapshots')\n  assert(snapshots[0].request.url.startsWith('https://'), 'Should use HTTPS')\n})\n</code></pre>",
              "displayName": "4. Snapshot Validation"
            }
          ],
          "displayName": "Best Practices"
        },
        {
          "textRaw": "Comparison with Other Tools",
          "name": "comparison_with_other_tools",
          "type": "module",
          "modules": [
            {
              "textRaw": "vs Manual MockAgent Setup",
              "name": "vs_manual_mockagent_setup",
              "type": "module",
              "desc": "<p><strong>Manual MockAgent:</strong></p>\n<pre><code class=\"language-javascript\">const mockAgent = new MockAgent()\nconst mockPool = mockAgent.get('https://api.example.com')\n\nmockPool.intercept({\n  path: '/users',\n  method: 'GET'\n}).reply(200, [\n  { id: 1, name: 'User 1' },\n  { id: 2, name: 'User 2' }\n])\n</code></pre>\n<p><strong>SnapshotAgent:</strong></p>\n<pre><code class=\"language-javascript\">// Record once\nconst agent = new SnapshotAgent({ mode: 'record', snapshotPath: './snapshots.json' })\n// Real API call gets recorded automatically\n\n// Use in tests\nconst agent = new SnapshotAgent({ mode: 'playback', snapshotPath: './snapshots.json' })\n// Automatically replays recorded response\n</code></pre>",
              "displayName": "vs Manual MockAgent Setup"
            },
            {
              "textRaw": "vs nock",
              "name": "vs_nock",
              "type": "module",
              "desc": "<p>SnapshotAgent provides similar functionality to nock but is specifically designed for undici:</p>\n<ul>\n<li>✅ Works with all undici APIs (<code>request</code>, <code>stream</code>, <code>pipeline</code>, etc.)</li>\n<li>✅ Supports undici-specific features (RetryAgent, connection pooling)</li>\n<li>✅ Better TypeScript integration</li>\n<li>✅ More efficient for high-performance scenarios</li>\n</ul>",
              "displayName": "vs nock"
            }
          ],
          "displayName": "Comparison with Other Tools"
        },
        {
          "textRaw": "See Also",
          "name": "see_also",
          "type": "module",
          "desc": "<ul>\n<li><a href=\"/docs/docs/api/MockAgent.html\">MockAgent</a> - Manual mocking for more control</li>\n<li><a href=\"/docs/docs/api/MockCallHistory.html\">MockCallHistory</a> - Inspecting request history</li>\n<li><a href=\"/docs/docs/best-practices/writing-tests.html\">Testing Best Practices</a> - General testing guidance</li>\n</ul>",
          "displayName": "See Also"
        }
      ],
      "displayName": "SnapshotAgent"
    }
  ]
}